EricWF created this revision.
EricWF added a reviewer: mclow.lists.
EricWF added a subscriber: cfe-commits.

Exactly what it sounds like.

I plan to commit this in a couple of days assuming no objections.

http://reviews.llvm.org/D20799

Files:
  include/functional
  test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp
  www/cxx1z_status.html

Index: www/cxx1z_status.html
===================================================================
--- www/cxx1z_status.html
+++ www/cxx1z_status.html
@@ -83,7 +83,7 @@
 	<tr><td><a href="http://wg21.link/P0220R1";>P0220R1</a></td><td>LWG</td><td>Adopt Library Fundamentals V1 TS Components for C++17</td><td>Jacksonville</td><td></td><td></td></tr>
 	<tr><td><a href="http://wg21.link/P0218R1";>P0218R1</a></td><td>LWG</td><td>Adopt the File System TS for C++17</td><td>Jacksonville</td><td></td><td></td></tr>	
 	<tr><td><a href="http://wg21.link/P0033R1";>P0033R1</a></td><td>LWG</td><td>Re-enabling shared_from_this</td><td>Jacksonville</td><td></td><td></td></tr>
-	<tr><td><a href="http://wg21.link/P0005R4";>P0005R4</a></td><td>LWG</td><td>Adopt not_fn from Library Fundamentals 2 for C++17</td><td>Jacksonville</td><td></td><td></td></tr>
+	<tr><td><a href="http://wg21.link/P0005R4";>P0005R4</a></td><td>LWG</td><td>Adopt not_fn from Library Fundamentals 2 for C++17</td><td>Jacksonville</td><td>Complete</td><td>3.9</td></tr>
 	<tr><td><a href="http://wg21.link/P0152R1";>P0152R1</a></td><td>LWG</td><td>constexpr atomic::is_always_lock_free</td><td>Jacksonville</td><td>Complete</td><td>3.9</td></tr>
 	<tr><td><a href="http://wg21.link/P0185R1";>P0185R1</a></td><td>LWG</td><td>Adding [nothrow-]swappable traits</td><td>Jacksonville</td><td>Complete</td><td>3.9</td></tr>
 	<tr><td><a href="http://wg21.link/P0253R1";>P0253R1</a></td><td>LWG</td><td>Fixing a design mistake in the searchers interface</td><td>Jacksonville</td><td>Complete</td><td>3.9</td></tr>
Index: test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp
===================================================================
--- /dev/null
+++ test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp
@@ -0,0 +1,459 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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;
+};
+
+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>;
+
+  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;
+};
+
+
+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(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));
+    }
+}
+
+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();
+}
Index: include/functional
===================================================================
--- include/functional
+++ include/functional
@@ -207,6 +207,8 @@
 
 template <class Predicate> binary_negate<Predicate> not2(const Predicate& pred);
 
+template <class F> unspecified not_fn(F&& f); // C++17
+
 template<class T> struct is_bind_expression;
 template<class T> struct is_placeholder;
 
@@ -2585,11 +2587,50 @@
 
 
 #if _LIBCPP_STD_VER > 14
+
 template <class _Fn, class ..._Args>
 result_of_t<_Fn&&(_Args&&...)>
 invoke(_Fn&& __f, _Args&&... __args) {
-    return __invoke(_VSTD::forward<_Fn>(__f), _VSTD::forward<_Args>(__args)...);
+    return _VSTD::__invoke(_VSTD::forward<_Fn>(__f), _VSTD::forward<_Args>(__args)...);
+}
+
+template <class _DecayFunc>
+class _LIBCPP_TYPE_VIS_ONLY __not_fn_imp {
+  _DecayFunc __fd;
+
+public:
+    __not_fn_imp() = delete;
+
+    template <class ..._Args>
+    _LIBCPP_INLINE_VISIBILITY
+    auto operator()(_Args&& ...__args)
+        -> decltype(!_VSTD::invoke(__fd, _VSTD::forward<_Args>(__args)...))
+        { return !_VSTD::invoke(__fd, _VSTD::forward<_Args>(__args)...); }
+
+    template <class ..._Args>
+    _LIBCPP_INLINE_VISIBILITY
+    auto operator()(_Args&& ...__args) const
+        -> decltype(!_VSTD::invoke(__fd, _VSTD::forward<_Args>(__args)...))
+        { return !_VSTD::invoke(__fd, _VSTD::forward<_Args>(__args)...); }
+
+private:
+    template <class _RawFunc,
+              class = enable_if_t<!is_same<decay_t<_RawFunc>, __not_fn_imp>::value>>
+    _LIBCPP_INLINE_VISIBILITY
+    explicit __not_fn_imp(_RawFunc&& __rf)
+        : __fd(_VSTD::forward<_RawFunc>(__rf)) {}
+
+    template <class _RawFunc>
+    friend inline _LIBCPP_INLINE_VISIBILITY
+    __not_fn_imp<decay_t<_RawFunc>> not_fn(_RawFunc&&);
+};
+
+template <class _RawFunc>
+inline _LIBCPP_INLINE_VISIBILITY
+__not_fn_imp<decay_t<_RawFunc>> not_fn(_RawFunc&& __fn) {
+    return __not_fn_imp<decay_t<_RawFunc>>(_VSTD::forward<_RawFunc>(__fn));
 }
+
 #endif
 
 // struct hash<T*> in <memory>
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to