summaryrefslogtreecommitdiffstats
path: root/test/std/utilities/function.objects
diff options
context:
space:
mode:
authorEric Fiselier <eric@efcs.ca>2016-06-02 01:25:41 +0000
committerEric Fiselier <eric@efcs.ca>2016-06-02 01:25:41 +0000
commitc230822a618b77d4ceee30ac88284a5a15820635 (patch)
tree019cbccfa9531d842fc3e723ecec72087044dc4e /test/std/utilities/function.objects
parenta27cbf8a138f32939ea48070ccdbdcf6ab9bfe8f (diff)
downloadexternal_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.cpp38
-rw-r--r--test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp540
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();
+}