diff options
author | Eric Fiselier <eric@efcs.ca> | 2016-06-02 01:25:41 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2016-06-02 01:25:41 +0000 |
commit | c230822a618b77d4ceee30ac88284a5a15820635 (patch) | |
tree | 019cbccfa9531d842fc3e723ecec72087044dc4e /test/std/utilities/function.objects | |
parent | a27cbf8a138f32939ea48070ccdbdcf6ab9bfe8f (diff) | |
download | external_libcxx-c230822a618b77d4ceee30ac88284a5a15820635.tar.gz external_libcxx-c230822a618b77d4ceee30ac88284a5a15820635.tar.bz2 external_libcxx-c230822a618b77d4ceee30ac88284a5a15820635.zip |
Add C++17 std::not_fn negator.
Summary:
Exactly what it sounds like.
I plan to commit this in a couple of days assuming no objections.
Reviewers: mclow.lists, EricWF
Subscribers: cfe-commits
Differential Revision: http://reviews.llvm.org/D20799
git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@271464 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/std/utilities/function.objects')
-rw-r--r-- | test/std/utilities/function.objects/func.invoke/invoke.pass.cpp | 38 | ||||
-rw-r--r-- | test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp | 540 |
2 files changed, 578 insertions, 0 deletions
diff --git a/test/std/utilities/function.objects/func.invoke/invoke.pass.cpp b/test/std/utilities/function.objects/func.invoke/invoke.pass.cpp index 3f02c7c4c..e78323f5a 100644 --- a/test/std/utilities/function.objects/func.invoke/invoke.pass.cpp +++ b/test/std/utilities/function.objects/func.invoke/invoke.pass.cpp @@ -304,8 +304,46 @@ void bullet_five_tests() { } } +struct CopyThrows { + CopyThrows() {} + CopyThrows(CopyThrows const&) {} + CopyThrows(CopyThrows&&) noexcept {} +}; + +struct NoThrowCallable { + void operator()() noexcept {} + void operator()(CopyThrows) noexcept {} +}; + +struct ThrowsCallable { + void operator()() {} +}; + +struct MemberObj { + int x; +}; + +void noexcept_test() { + { + NoThrowCallable obj; + CopyThrows arg; + static_assert(noexcept(std::invoke(obj))); + static_assert(!noexcept(std::invoke(obj, arg))); + static_assert(noexcept(std::invoke(obj, std::move(arg)))); + } + { + ThrowsCallable obj; + static_assert(!noexcept(std::invoke(obj))); + } + { + MemberObj obj{42}; + static_assert(noexcept(std::invoke(&MemberObj::x, obj))); + } +} + int main() { bullet_one_two_tests(); bullet_three_four_tests(); bullet_five_tests(); + noexcept_test(); } diff --git a/test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp b/test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp new file mode 100644 index 000000000..a338388fe --- /dev/null +++ b/test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp @@ -0,0 +1,540 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +// template <class F> unspecified not_fn(F&& f); + +#include <functional> +#include <type_traits> +#include <string> +#include <cassert> + +#include "test_macros.h" +#include "type_id.h" + + +/////////////////////////////////////////////////////////////////////////////// +// CALLABLE TEST TYPES +/////////////////////////////////////////////////////////////////////////////// + +bool returns_true() { return true; } + +template <class Ret = bool> +struct MoveOnlyCallable { + MoveOnlyCallable(MoveOnlyCallable const&) = delete; + MoveOnlyCallable(MoveOnlyCallable&& other) + : value(other.value) + { other.value = !other.value; } + + template <class ...Args> + Ret operator()(Args&&...) { return Ret{value}; } + + explicit MoveOnlyCallable(bool x) : value(x) {} + Ret value; +}; + +template <class Ret = bool> +struct CopyCallable { + CopyCallable(CopyCallable const& other) + : value(other.value) {} + + CopyCallable(CopyCallable&& other) + : value(other.value) { other.value = !other.value; } + + template <class ...Args> + Ret operator()(Args&&...) { return Ret{value}; } + + explicit CopyCallable(bool x) : value(x) {} + Ret value; +}; + + +template <class Ret = bool> +struct ConstCallable { + ConstCallable(ConstCallable const& other) + : value(other.value) {} + + ConstCallable(ConstCallable&& other) + : value(other.value) { other.value = !other.value; } + + template <class ...Args> + Ret operator()(Args&&...) const { return Ret{value}; } + + explicit ConstCallable(bool x) : value(x) {} + Ret value; +}; + + + +template <class Ret = bool> +struct NoExceptCallable { + NoExceptCallable(NoExceptCallable const& other) + : value(other.value) {} + + template <class ...Args> + Ret operator()(Args&&...) noexcept { return Ret{value}; } + + template <class ...Args> + Ret operator()(Args&&...) const noexcept { return Ret{value}; } + + explicit NoExceptCallable(bool x) : value(x) {} + Ret value; +}; + + +struct CopyAssignableWrapper { + CopyAssignableWrapper(CopyAssignableWrapper const&) = default; + CopyAssignableWrapper(CopyAssignableWrapper&&) = default; + CopyAssignableWrapper& operator=(CopyAssignableWrapper const&) = default; + CopyAssignableWrapper& operator=(CopyAssignableWrapper &&) = default; + + template <class ...Args> + bool operator()(Args&&...) { return value; } + + explicit CopyAssignableWrapper(bool x) : value(x) {} + bool value; +}; + + +struct MoveAssignableWrapper { + MoveAssignableWrapper(MoveAssignableWrapper const&) = delete; + MoveAssignableWrapper(MoveAssignableWrapper&&) = default; + MoveAssignableWrapper& operator=(MoveAssignableWrapper const&) = delete; + MoveAssignableWrapper& operator=(MoveAssignableWrapper &&) = default; + + template <class ...Args> + bool operator()(Args&&...) { return value; } + + explicit MoveAssignableWrapper(bool x) : value(x) {} + bool value; +}; + +struct MemFunCallable { + explicit MemFunCallable(bool x) : value(x) {} + + bool return_value() const { return value; } + bool return_value_nc() { return value; } + bool value; +}; + +enum CallType { + CT_None, + CT_Const, + CT_NonConst +}; + +struct ForwardingCallObject { + + template <class ...Args> + bool operator()(Args&&... args) & { + set_call<Args&&...>(CT_NonConst); + return true; + } + + template <class ...Args> + bool operator()(Args&&... args) const & { + set_call<Args&&...>(CT_Const); + return true; + } + + // Don't allow the call operator to be invoked as an rvalue. + template <class ...Args> + bool operator()(Args&&... args) && = delete; + + template <class ...Args> + bool operator()(Args&&... args) const && = delete; + + template <class ...Args> + static void set_call(CallType type) { + assert(last_call_type == CT_None); + assert(last_call_args == nullptr); + last_call_type = type; + last_call_args = &makeArgumentID<Args...>(); + } + + template <class ...Args> + static bool check_call(CallType type) { + bool result = + last_call_type == type + && last_call_args + && *last_call_args == makeArgumentID<Args...>(); + last_call_type = CT_None; + last_call_args = nullptr; + return result; + } + + static CallType last_call_type; + static TypeID const* last_call_args; +}; + +CallType ForwardingCallObject::last_call_type = CT_None; +TypeID const* ForwardingCallObject::last_call_args = nullptr; + + + +/////////////////////////////////////////////////////////////////////////////// +// BOOL TEST TYPES +/////////////////////////////////////////////////////////////////////////////// + +struct EvilBool { + static int bang_called; + + EvilBool(EvilBool const&) = default; + EvilBool(EvilBool&&) = default; + + friend EvilBool operator!(EvilBool const& other) { + ++bang_called; + return EvilBool{!other.value}; + } + +private: + friend struct MoveOnlyCallable<EvilBool>; + friend struct CopyCallable<EvilBool>; + friend struct NoExceptCallable<EvilBool>; + + explicit EvilBool(bool x) : value(x) {} + EvilBool& operator=(EvilBool const& other) = default; + +public: + bool value; +}; + +int EvilBool::bang_called = 0; + +struct ExplicitBool { + ExplicitBool(ExplicitBool const&) = default; + ExplicitBool(ExplicitBool&&) = default; + + explicit operator bool() const { return value; } + +private: + friend struct MoveOnlyCallable<ExplicitBool>; + friend struct CopyCallable<ExplicitBool>; + + explicit ExplicitBool(bool x) : value(x) {} + ExplicitBool& operator=(bool x) { + value = x; + return *this; + } + + bool value; +}; + + +struct NoExceptEvilBool { + NoExceptEvilBool(NoExceptEvilBool const&) = default; + NoExceptEvilBool(NoExceptEvilBool&&) = default; + NoExceptEvilBool& operator=(NoExceptEvilBool const& other) = default; + + explicit NoExceptEvilBool(bool x) : value(x) {} + + friend NoExceptEvilBool operator!(NoExceptEvilBool const& other) noexcept { + return NoExceptEvilBool{!other.value}; + } + + bool value; +}; + + + +void constructor_tests() +{ + { + using T = MoveOnlyCallable<bool>; + T value(true); + using RetT = decltype(std::not_fn(std::move(value))); + static_assert(std::is_move_constructible<RetT>::value); + static_assert(!std::is_copy_constructible<RetT>::value); + static_assert(!std::is_move_assignable<RetT>::value); + static_assert(!std::is_copy_assignable<RetT>::value); + auto ret = std::not_fn(std::move(value)); + // test it was moved from + assert(value.value == false); + // test that ret() negates the original value 'true' + assert(ret() == false); + assert(ret(0, 0.0, "blah") == false); + // Move ret and test that it was moved from and that ret2 got the + // original value. + auto ret2 = std::move(ret); + assert(ret() == true); + assert(ret2() == false); + assert(ret2(42) == false); + } + { + using T = CopyCallable<bool>; + T value(false); + using RetT = decltype(std::not_fn(value)); + static_assert(std::is_move_constructible<RetT>::value); + static_assert(std::is_copy_constructible<RetT>::value); + static_assert(!std::is_move_assignable<RetT>::value); + static_assert(!std::is_copy_assignable<RetT>::value); + auto ret = std::not_fn(value); + // test that value is unchanged (copied not moved) + assert(value.value == false); + // test 'ret' has the original value + assert(ret() == true); + assert(ret(42, 100) == true); + // move from 'ret' and check that 'ret2' has the original value. + auto ret2 = std::move(ret); + assert(ret() == false); + assert(ret2() == true); + assert(ret2("abc") == true); + } + { + using T = CopyAssignableWrapper; + T value(true); + T value2(false); + using RetT = decltype(std::not_fn(value)); + static_assert(std::is_move_constructible<RetT>::value); + static_assert(std::is_copy_constructible<RetT>::value); + static_assert(std::is_move_assignable<RetT>::value); + static_assert(std::is_copy_assignable<RetT>::value); + auto ret = std::not_fn(value); + assert(ret() == false); + auto ret2 = std::not_fn(value2); + assert(ret2() == true); + ret = ret2; + assert(ret() == true); + assert(ret2() == true); + } + { + using T = MoveAssignableWrapper; + T value(true); + T value2(false); + using RetT = decltype(std::not_fn(std::move(value))); + static_assert(std::is_move_constructible<RetT>::value); + static_assert(!std::is_copy_constructible<RetT>::value); + static_assert(std::is_move_assignable<RetT>::value); + static_assert(!std::is_copy_assignable<RetT>::value); + auto ret = std::not_fn(std::move(value)); + assert(ret() == false); + auto ret2 = std::not_fn(std::move(value2)); + assert(ret2() == true); + ret = std::move(ret2); + assert(ret() == true); + } +} + +void return_type_tests() +{ + using std::is_same; + { + using T = CopyCallable<bool>; + auto ret = std::not_fn(T{false}); + static_assert(is_same<decltype(ret()), bool>::value); + static_assert(is_same<decltype(ret("abc")), bool>::value); + assert(ret() == true); + } + { + using T = CopyCallable<ExplicitBool>; + auto ret = std::not_fn(T{true}); + static_assert(is_same<decltype(ret()), bool>::value); + static_assert(is_same<decltype(ret(std::string("abc"))), bool>::value); + assert(ret() == false); + } + { + using T = CopyCallable<EvilBool>; + auto ret = std::not_fn(T{false}); + static_assert(is_same<decltype(ret()), EvilBool>::value); + EvilBool::bang_called = 0; + auto value_ret = ret(); + assert(EvilBool::bang_called == 1); + assert(value_ret.value == true); + ret(); + assert(EvilBool::bang_called == 2); + } +} + +// Other tests only test using objects with call operators. Test various +// other callable types here. +void other_callable_types_test() +{ + { // test with function pointer + auto ret = std::not_fn(returns_true); + assert(ret() == false); + } + { // test with lambda + auto returns_value = [](bool value) { return value; }; + auto ret = std::not_fn(returns_value); + assert(ret(true) == false); + assert(ret(false) == true); + } + { // test with pointer to member function + MemFunCallable mt(true); + const MemFunCallable mf(false); + auto ret = std::not_fn(&MemFunCallable::return_value); + assert(ret(mt) == false); + assert(ret(mf) == true); + assert(ret(&mt) == false); + assert(ret(&mf) == true); + } + { // test with pointer to member function + MemFunCallable mt(true); + MemFunCallable mf(false); + auto ret = std::not_fn(&MemFunCallable::return_value_nc); + assert(ret(mt) == false); + assert(ret(mf) == true); + assert(ret(&mt) == false); + assert(ret(&mf) == true); + } + { // test with pointer to member data + MemFunCallable mt(true); + const MemFunCallable mf(false); + auto ret = std::not_fn(&MemFunCallable::value); + assert(ret(mt) == false); + assert(ret(mf) == true); + assert(ret(&mt) == false); + assert(ret(&mf) == true); + } +} + +void throws_in_constructor_test() +{ +#ifndef TEST_HAS_NO_EXCEPTIONS + struct ThrowsOnCopy { + ThrowsOnCopy(ThrowsOnCopy const&) { + throw 42; + } + ThrowsOnCopy() = default; + bool operator()() const { assert(false); } + }; + { + ThrowsOnCopy cp; + try { + std::not_fn(cp); + assert(false); + } catch (int const& value) { + assert(value == 42); + } + } +#endif +} + +void call_operator_sfinae_test() { + { // wrong number of arguments + using T = decltype(std::not_fn(returns_true)); + static_assert(std::is_callable<T()>::value); // callable only with no args + static_assert(!std::is_callable<T(bool)>::value); + } + { // violates const correctness (member function pointer) + using T = decltype(std::not_fn(&MemFunCallable::return_value_nc)); + static_assert(std::is_callable<T(MemFunCallable&)>::value); + static_assert(!std::is_callable<T(const MemFunCallable&)>::value); + } + { // violates const correctness (call object) + using Obj = CopyCallable<bool>; + using NCT = decltype(std::not_fn(Obj{true})); + using CT = const NCT; + static_assert(std::is_callable<NCT()>::value); + static_assert(!std::is_callable<CT()>::value); + } + { // returns bad type with no operator! + auto fn = [](auto x) { return x; }; + using T = decltype(std::not_fn(fn)); + static_assert(std::is_callable<T(bool)>::value); + static_assert(!std::is_callable<T(std::string)>::value); + } +} + +void call_operator_forwarding_test() +{ + using Fn = ForwardingCallObject; + auto obj = std::not_fn(Fn{}); + const auto& c_obj = obj; + { // test zero args + obj(); + assert(Fn::check_call<>(CT_NonConst)); + c_obj(); + assert(Fn::check_call<>(CT_Const)); + } + { // test value categories + int x = 42; + const int cx = 42; + obj(x); + assert(Fn::check_call<int&>(CT_NonConst)); + obj(cx); + assert(Fn::check_call<const int&>(CT_NonConst)); + obj(std::move(x)); + assert(Fn::check_call<int&&>(CT_NonConst)); + obj(std::move(cx)); + assert(Fn::check_call<const int&&>(CT_NonConst)); + obj(42); + assert(Fn::check_call<int&&>(CT_NonConst)); + } + { // test value categories - const call + int x = 42; + const int cx = 42; + c_obj(x); + assert(Fn::check_call<int&>(CT_Const)); + c_obj(cx); + assert(Fn::check_call<const int&>(CT_Const)); + c_obj(std::move(x)); + assert(Fn::check_call<int&&>(CT_Const)); + c_obj(std::move(cx)); + assert(Fn::check_call<const int&&>(CT_Const)); + c_obj(42); + assert(Fn::check_call<int&&>(CT_Const)); + } + { // test multi arg + int x = 42; + const double y = 3.14; + std::string s = "abc"; + obj(42, std::move(y), s, std::string{"foo"}); + Fn::check_call<int&&, const double&&, std::string&, std::string&&>(CT_NonConst); + c_obj(42, std::move(y), s, std::string{"foo"}); + Fn::check_call<int&&, const double&&, std::string&, std::string&&>(CT_Const); + } + { // call as rvalue test. This should not invoke the functor as an rvalue. + std::move(obj)(); + assert(Fn::check_call<>(CT_NonConst)); + std::move(c_obj)(); + assert(Fn::check_call<>(CT_Const)); + } +} + +void call_operator_noexcept_test() +{ + { + using T = ConstCallable<bool>; + T value(true); + auto ret = std::not_fn(value); + static_assert(!noexcept(ret()), "call should not be noexcept"); + auto const& cret = ret; + static_assert(!noexcept(cret()), "call should not be noexcept"); + } + { + using T = NoExceptCallable<bool>; + T value(true); + auto ret = std::not_fn(value); + static_assert(noexcept(!_VSTD::__invoke(value)), ""); + static_assert(noexcept(ret()), "call should be noexcept"); + auto const& cret = ret; + static_assert(noexcept(cret()), "call should be noexcept"); + } + { + using T = NoExceptCallable<EvilBool>; + T value(true); + auto ret = std::not_fn(value); + static_assert(!noexcept(ret()), "call should not be noexcept"); + auto const& cret = ret; + static_assert(!noexcept(cret()), "call should not be noexcept"); + } +} + +int main() +{ + constructor_tests(); + return_type_tests(); + other_callable_types_test(); + throws_in_constructor_test(); + call_operator_sfinae_test(); // somewhat of an extension + call_operator_forwarding_test(); + call_operator_noexcept_test(); +} |