123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- //===----------------------------------------------------------------------===//
- //
- // 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 : unsigned {
- CT_None,
- CT_NonConst = 1,
- CT_Const = 2,
- CT_LValue = 4,
- CT_RValue = 8
- };
- inline constexpr CallType operator|(CallType LHS, CallType RHS) {
- return static_cast<CallType>(static_cast<unsigned>(LHS) | static_cast<unsigned>(RHS));
- }
- struct ForwardingCallObject {
- template <class ...Args>
- bool operator()(Args&&...) & {
- set_call<Args&&...>(CT_NonConst | CT_LValue);
- return true;
- }
- template <class ...Args>
- bool operator()(Args&&...) const & {
- set_call<Args&&...>(CT_Const | CT_LValue);
- return true;
- }
- // Don't allow the call operator to be invoked as an rvalue.
- template <class ...Args>
- bool operator()(Args&&...) && {
- set_call<Args&&...>(CT_NonConst | CT_RValue);
- return true;
- }
- template <class ...Args>
- bool operator()(Args&&...) const && {
- set_call<Args&&...>(CT_Const | CT_RValue);
- return true;
- }
- 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);
- #if defined(TEST_COMPILER_C1XX)
- __assume(0);
- #else
- __builtin_unreachable();
- #endif
- }
- };
- {
- 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_invocable<T>::value, ""); // callable only with no args
- static_assert(!std::is_invocable<T, bool>::value, "");
- }
- { // violates const correctness (member function pointer)
- using T = decltype(std::not_fn(&MemFunCallable::return_value_nc));
- static_assert(std::is_invocable<T, MemFunCallable&>::value, "");
- static_assert(!std::is_invocable<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_invocable<NCT>::value, "");
- static_assert(!std::is_invocable<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_invocable<T, bool>::value, "");
- static_assert(!std::is_invocable<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 | CT_LValue));
- std::move(obj)();
- assert(Fn::check_call<>(CT_NonConst | CT_RValue));
- c_obj();
- assert(Fn::check_call<>(CT_Const | CT_LValue));
- std::move(c_obj)();
- assert(Fn::check_call<>(CT_Const | CT_RValue));
- }
- { // test value categories
- int x = 42;
- const int cx = 42;
- obj(x);
- assert(Fn::check_call<int&>(CT_NonConst | CT_LValue));
- obj(cx);
- assert(Fn::check_call<const int&>(CT_NonConst | CT_LValue));
- obj(std::move(x));
- assert(Fn::check_call<int&&>(CT_NonConst | CT_LValue));
- obj(std::move(cx));
- assert(Fn::check_call<const int&&>(CT_NonConst | CT_LValue));
- obj(42);
- assert(Fn::check_call<int&&>(CT_NonConst | CT_LValue));
- }
- { // test value categories - rvalue
- int x = 42;
- const int cx = 42;
- std::move(obj)(x);
- assert(Fn::check_call<int&>(CT_NonConst | CT_RValue));
- std::move(obj)(cx);
- assert(Fn::check_call<const int&>(CT_NonConst | CT_RValue));
- std::move(obj)(std::move(x));
- assert(Fn::check_call<int&&>(CT_NonConst | CT_RValue));
- std::move(obj)(std::move(cx));
- assert(Fn::check_call<const int&&>(CT_NonConst | CT_RValue));
- std::move(obj)(42);
- assert(Fn::check_call<int&&>(CT_NonConst | CT_RValue));
- }
- { // test value categories - const call
- int x = 42;
- const int cx = 42;
- c_obj(x);
- assert(Fn::check_call<int&>(CT_Const | CT_LValue));
- c_obj(cx);
- assert(Fn::check_call<const int&>(CT_Const | CT_LValue));
- c_obj(std::move(x));
- assert(Fn::check_call<int&&>(CT_Const | CT_LValue));
- c_obj(std::move(cx));
- assert(Fn::check_call<const int&&>(CT_Const | CT_LValue));
- c_obj(42);
- assert(Fn::check_call<int&&>(CT_Const | CT_LValue));
- }
- { // test value categories - const call rvalue
- int x = 42;
- const int cx = 42;
- std::move(c_obj)(x);
- assert(Fn::check_call<int&>(CT_Const | CT_RValue));
- std::move(c_obj)(cx);
- assert(Fn::check_call<const int&>(CT_Const | CT_RValue));
- std::move(c_obj)(std::move(x));
- assert(Fn::check_call<int&&>(CT_Const | CT_RValue));
- std::move(c_obj)(std::move(cx));
- assert(Fn::check_call<const int&&>(CT_Const | CT_RValue));
- std::move(c_obj)(42);
- assert(Fn::check_call<int&&>(CT_Const | CT_RValue));
- }
- { // test multi arg
- 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 | CT_LValue);
- std::move(obj)(42, std::move(y), s, std::string{"foo"});
- Fn::check_call<int&&, const double&&, std::string&, std::string&&>(CT_NonConst | CT_RValue);
- c_obj(42, std::move(y), s, std::string{"foo"});
- Fn::check_call<int&&, const double&&, std::string&, std::string&&>(CT_Const | CT_LValue);
- std::move(c_obj)(42, std::move(y), s, std::string{"foo"});
- Fn::check_call<int&&, const double&&, std::string&, std::string&&>(CT_Const | CT_RValue);
- }
- }
- 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);
- LIBCPP_STATIC_ASSERT(noexcept(!_VSTD::__invoke(value)), "");
- #if TEST_STD_VER > 14
- static_assert(noexcept(!std::invoke(value)), "");
- #endif
- static_assert(noexcept(ret()), "call should be noexcept");
- auto const& cret = ret;
- static_assert(noexcept(cret()), "call should be noexcept");
- }
- {
- using T = NoExceptCallable<NoExceptEvilBool>;
- 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<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");
- }
- }
- void test_lwg2767() {
- // See https://cplusplus.github.io/LWG/lwg-defects.html#2767
- struct Abstract { virtual void f() const = 0; };
- struct Derived : public Abstract { void f() const {} };
- struct F { bool operator()(Abstract&&) { return false; } };
- {
- Derived d;
- Abstract &a = d;
- bool b = std::not_fn(F{})(std::move(a));
- assert(b);
- }
- }
- 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();
- test_lwg2767();
- }
|