Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++][functional] Implement not_fn<NTTP> #86133

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

JMazurkiewicz
Copy link
Contributor

@JMazurkiewicz JMazurkiewicz commented Mar 21, 2024

Implement not_fn<NTTP> function from "P2714R1 Bind front and back to NTTP callables".

Current implementation does not use std::__perfect_forward because not_fn<NTTP> does not have state entities.

@JMazurkiewicz JMazurkiewicz requested a review from a team as a code owner March 21, 2024 15:28
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Mar 21, 2024
@llvmbot
Copy link

llvmbot commented Mar 21, 2024

@llvm/pr-subscribers-libcxx

Author: Jakub Mazurkiewicz (JMazurkiewicz)

Changes

Implement not_fn&lt;NTTP&gt; function from P2714R1 Bind front and back to NTTP callables.

Current implementation does not use std::__perfect_forward because not_fn&lt;NTTP&gt; does not have state entities.


Patch is 22.79 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/86133.diff

13 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+2)
  • (modified) libcxx/docs/Status/Cxx2c.rst (+1)
  • (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1)
  • (modified) libcxx/include/__functional/not_fn.h (+23)
  • (modified) libcxx/include/functional (+3-1)
  • (modified) libcxx/include/version (+4-1)
  • (added) libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.compile.pass.cpp (+29)
  • (added) libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.nodiscard.verify.cpp (+21)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp (+3-2)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+3-2)
  • (added) libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.pass.cpp (+294)
  • (added) libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.verify.cpp (+29)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+2-2)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index b213f430aa5922..bdf9e57890f90c 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -428,6 +428,8 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_linalg``                                *unimplemented*
     --------------------------------------------------- -----------------
+    ``__cpp_lib_not_fn``                                ``202306L``
+    --------------------------------------------------- -----------------
     ``__cpp_lib_out_ptr``                               *unimplemented*
     --------------------------------------------------- -----------------
     ``__cpp_lib_ratio``                                 ``202306L``
diff --git a/libcxx/docs/Status/Cxx2c.rst b/libcxx/docs/Status/Cxx2c.rst
index a7ebc4662f517c..81eeee3367d8c2 100644
--- a/libcxx/docs/Status/Cxx2c.rst
+++ b/libcxx/docs/Status/Cxx2c.rst
@@ -40,6 +40,7 @@ Paper Status
 .. note::
 
    .. [#note-P2510R3] This paper is applied as DR against C++20. (MSVC STL and libstdc++ will do the same.)
+   .. [#note-P2714R1] ``not_fn`` is done.
 
 .. _issues-status-cxx2c:
 
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 4a5443dea115c8..a085dc899852f9 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -24,7 +24,7 @@
 "`P1383R2 <https://wg21.link/P1383R2>`__","LWG","More ``constexpr`` for ``<cmath>`` and ``<complex>``","Varna June 2023","","",""
 "`P2734R0 <https://wg21.link/P2734R0>`__","LWG","Adding the new SI prefixes","Varna June 2023","|Complete|","17.0",""
 "`P2548R6 <https://wg21.link/P2548R6>`__","LWG","``copyable_function``","Varna June 2023","","",""
-"`P2714R1 <https://wg21.link/P2714R1>`__","LWG","Bind front and back to NTTP callables","Varna June 2023","","",""
+"`P2714R1 <https://wg21.link/P2714R1>`__","LWG","Bind front and back to NTTP callables","Varna June 2023","|Partial| [#note-P2714R1]_","19.0",""
 "`P2630R4 <https://wg21.link/P2630R4>`__","LWG","``submdspan``","Varna June 2023","","",""
 "","","","","","",""
 "`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","|Complete|","18.0",""
diff --git a/libcxx/include/__functional/not_fn.h b/libcxx/include/__functional/not_fn.h
index 4b3ce5524a7434..d5e6d5f9939d2d 100644
--- a/libcxx/include/__functional/not_fn.h
+++ b/libcxx/include/__functional/not_fn.h
@@ -16,6 +16,8 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_constructible.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_pointer.h>
 #include <__utility/forward.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -48,6 +50,27 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 auto not_fn(_Fn&& __f) {
 
 #endif // _LIBCPP_STD_VER >= 17
 
+#if _LIBCPP_STD_VER >= 26
+
+template <auto _Fn>
+struct __nttp_not_fn_t {
+  template <class... _Args>
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Args&&... __args) const
+      noexcept(noexcept(!std::invoke(_Fn, std::forward<_Args>(__args)...)))
+          -> decltype(!std::invoke(_Fn, std::forward<_Args>(__args)...)) {
+    return !std::invoke(_Fn, std::forward<_Args>(__args)...);
+  }
+};
+
+template <auto _Fn>
+_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto not_fn() noexcept {
+  if constexpr (using _Ty = decltype(_Fn); is_pointer_v<_Ty> || is_member_pointer_v<_Ty>)
+    static_assert(_Fn != nullptr, "f cannot be equal to nullptr");
+  return __nttp_not_fn_t<_Fn>();
+}
+
+#endif // _LIBCPP_STD_VER >= 26
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___FUNCTIONAL_NOT_FN_H
diff --git a/libcxx/include/functional b/libcxx/include/functional
index a2774a48bda0ee..faf97f17daec3d 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -205,7 +205,9 @@ template <class Predicate> // deprecated in C++17, removed in C++20
 binary_negate<Predicate> not2(const Predicate& pred);
 
 template <class F>
-constexpr unspecified not_fn(F&& f); // C++17, constexpr in C++20
+  constexpr unspecified not_fn(F&& f);     // C++17, constexpr in C++20
+template <auto f>
+  constexpr unspecified not_fn() noexcept; // C++26
 
 template<class T> struct is_bind_expression;
 template<class T> struct is_placeholder;
diff --git a/libcxx/include/version b/libcxx/include/version
index 3bd296e34aa4e3..8fda253f4051d3 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -154,7 +154,8 @@ __cpp_lib_nonmember_container_access                    201411L <array> <deque>
                                                                 <iterator> <list> <map>
                                                                 <regex> <set> <string>
                                                                 <unordered_map> <unordered_set> <vector>
-__cpp_lib_not_fn                                        201603L <functional>
+__cpp_lib_not_fn                                        202306L <functional>
+                                                        201603L // C++17
 __cpp_lib_null_iterators                                201304L <iterator>
 __cpp_lib_optional                                      202110L <optional>
                                                         201606L // C++17
@@ -507,6 +508,8 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 // # define __cpp_lib_function_ref                         202306L
 // # define __cpp_lib_hazard_pointer                       202306L
 // # define __cpp_lib_linalg                               202311L
+# undef  __cpp_lib_not_fn
+# define __cpp_lib_not_fn                               202306L
 # undef  __cpp_lib_out_ptr
 // # define __cpp_lib_out_ptr                              202311L
 # define __cpp_lib_ratio                                202306L
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.compile.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.compile.pass.cpp
new file mode 100644
index 00000000000000..8390bd76f5723d
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.compile.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <functional>
+
+// Test implementation-defined properties of std::not_fn<NTTP>.
+
+#include <functional>
+#include <type_traits>
+
+struct NotEmptyFunctionObject {
+  bool val = true;
+  bool operator()() const; // not defined
+};
+
+void test() {
+  using ResultWithEmptyFuncObject = decltype(std::not_fn<std::false_type{}>());
+  static_assert(std::is_empty_v<ResultWithEmptyFuncObject>);
+
+  using ResultWithNotEmptyFuncObject = decltype(std::not_fn<NotEmptyFunctionObject{}>());
+  static_assert(std::is_empty_v<ResultWithNotEmptyFuncObject>);
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.nodiscard.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..50d956e73fc09d
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.not.fn/not_fn.nttp.nodiscard.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <functional>
+
+// Test the libc++ extension that std::not_fn<NTTP> is marked as [[nodiscard]].
+
+#include <functional>
+#include <type_traits>
+
+void test() {
+  using F = std::true_type;
+  std::not_fn<F{}>(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index 72c96c62b64c45..a1252288159a1c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -28,6 +28,7 @@
     __cpp_lib_invoke_r                 202106L [C++23]
     __cpp_lib_move_only_function       202110L [C++23]
     __cpp_lib_not_fn                   201603L [C++17]
+                                       202306L [C++26]
     __cpp_lib_ranges                   202207L [C++20]
     __cpp_lib_result_of_sfinae         201210L [C++14]
     __cpp_lib_transparent_operators    201210L [C++14]
@@ -516,8 +517,8 @@
 # ifndef __cpp_lib_not_fn
 #   error "__cpp_lib_not_fn should be defined in c++26"
 # endif
-# if __cpp_lib_not_fn != 201603L
-#   error "__cpp_lib_not_fn should have the value 201603L in c++26"
+# if __cpp_lib_not_fn != 202306L
+#   error "__cpp_lib_not_fn should have the value 202306L in c++26"
 # endif
 
 # ifndef __cpp_lib_ranges
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 5501587915ffa0..72192a7673a038 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -144,6 +144,7 @@
     __cpp_lib_node_extract                           201606L [C++17]
     __cpp_lib_nonmember_container_access             201411L [C++17]
     __cpp_lib_not_fn                                 201603L [C++17]
+                                                     202306L [C++26]
     __cpp_lib_null_iterators                         201304L [C++14]
     __cpp_lib_optional                               201606L [C++17]
                                                      202110L [C++23]
@@ -6968,8 +6969,8 @@
 # ifndef __cpp_lib_not_fn
 #   error "__cpp_lib_not_fn should be defined in c++26"
 # endif
-# if __cpp_lib_not_fn != 201603L
-#   error "__cpp_lib_not_fn should have the value 201603L in c++26"
+# if __cpp_lib_not_fn != 202306L
+#   error "__cpp_lib_not_fn should have the value 202306L in c++26"
 # endif
 
 # ifndef __cpp_lib_null_iterators
diff --git a/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.pass.cpp b/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.pass.cpp
new file mode 100644
index 00000000000000..29df597704e1e8
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.nttp.pass.cpp
@@ -0,0 +1,294 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <functional>
+
+// template<auto f> constexpr unspecified not_fn() noexcept;
+
+#include <functional>
+
+#include <bit>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+class BooleanTestable {
+  bool val_;
+
+public:
+  constexpr explicit BooleanTestable(bool val) : val_(val) {}
+  constexpr operator bool() const { return val_; }
+  constexpr BooleanTestable operator!() const { return BooleanTestable{!val_}; }
+};
+
+LIBCPP_STATIC_ASSERT(std::__boolean_testable<BooleanTestable>);
+
+class FakeBool {
+  int val_;
+
+public:
+  constexpr FakeBool(int val) : val_(val) {}
+  constexpr FakeBool operator!() const { return FakeBool{-val_}; }
+  constexpr bool operator==(int other) const { return val_ == other; }
+};
+
+template <bool IsNoexcept>
+struct MaybeNoexceptFn {
+  bool operator()() const noexcept(IsNoexcept); // not defined
+};
+
+constexpr void basic_tests() {
+  { // Test constant functions
+    auto false_fn = std::not_fn<std::false_type{}>();
+    assert(false_fn());
+
+    auto true_fn = std::not_fn<std::true_type{}>();
+    assert(!true_fn());
+
+    static_assert(noexcept(std::not_fn<std::false_type{}>()));
+    static_assert(noexcept(std::not_fn<std::true_type{}>()));
+  }
+
+  { // Test function with one argument
+    auto is_odd = std::not_fn<[](auto x) { return x % 2 == 0; }>();
+    assert(is_odd(1));
+    assert(!is_odd(2));
+    assert(is_odd(3));
+    assert(!is_odd(4));
+    assert(is_odd(5));
+  }
+
+  { // Test function with multiple arguments
+    auto at_least_10 = [](auto... vals) { return (vals + ... + 0) >= 10; };
+    auto at_most_9   = std::not_fn<at_least_10>();
+    assert(at_most_9());
+    assert(at_most_9(1));
+    assert(at_most_9(1, 2, 3, 4, -1));
+    assert(at_most_9(3, 3, 2, 1, -2));
+    assert(!at_most_9(10, -1, 2));
+    assert(!at_most_9(5, 5));
+    static_assert(noexcept(std::not_fn<at_least_10>()));
+  }
+
+  { // Test function that returns boolean-testable type other than bool
+    auto is_product_even = [](auto... vals) { return BooleanTestable{(vals * ... * 1) % 2 == 0}; };
+    auto is_product_odd  = std::not_fn<is_product_even>();
+    assert(is_product_odd());
+    assert(is_product_odd(1, 3, 5, 9));
+    assert(is_product_odd(3, 3, 3, 3));
+    assert(!is_product_odd(3, 5, 9, 11, 0));
+    assert(!is_product_odd(11, 7, 5, 3, 2));
+    static_assert(noexcept(std::not_fn<is_product_even>()));
+  }
+
+  { // Test function that returns non-boolean-testable type
+    auto sum         = [](auto... vals) -> FakeBool { return (vals + ... + 0); };
+    auto negated_sum = std::not_fn<sum>();
+    assert(negated_sum() == 0);
+    assert(negated_sum(3) == -3);
+    assert(negated_sum(4, 5, 1, 3) == -13);
+    assert(negated_sum(4, 2, 5, 6, 1) == -18);
+    assert(negated_sum(-1, 3, 2, -8) == 4);
+    static_assert(noexcept(std::not_fn<sum>()));
+  }
+
+  { // Test member pointers
+    struct MemberPointerTester {
+      bool value = true;
+      constexpr bool not_value() const { return !value; }
+      constexpr bool value_and(bool other) noexcept { return value && other; }
+    };
+
+    MemberPointerTester tester;
+
+    auto not_mem_object = std::not_fn<&MemberPointerTester::value>();
+    assert(!not_mem_object(tester));
+    assert(!not_mem_object(std::as_const(tester)));
+    static_assert(noexcept(not_mem_object(tester)));
+    static_assert(noexcept(not_mem_object(std::as_const(tester))));
+
+    auto not_nullary_mem_fn = std::not_fn<&MemberPointerTester::not_value>();
+    assert(not_nullary_mem_fn(tester));
+    static_assert(!noexcept(not_nullary_mem_fn(tester)));
+
+    auto not_unary_mem_fn = std::not_fn<&MemberPointerTester::value_and>();
+    assert(not_unary_mem_fn(tester, false));
+    static_assert(noexcept(not_unary_mem_fn(tester, false)));
+    static_assert(!std::is_invocable_v<decltype(not_unary_mem_fn), const MemberPointerTester&, bool>);
+  }
+}
+
+constexpr void test_perfect_forwarding_call_wrapper() {
+  { // Make sure we call the correctly cv-ref qualified operator()
+    // based on the value category of the not_fn<NTTP> unspecified-type.
+    struct X {
+      constexpr FakeBool operator()() & { return 1; }
+      constexpr FakeBool operator()() const& { return 2; }
+      constexpr FakeBool operator()() && { return 3; }
+      constexpr FakeBool operator()() const&& { return 4; }
+    };
+
+    auto f  = std::not_fn<X{}>();
+    using F = decltype(f);
+    assert(static_cast<F&>(f)() == -2);
+    assert(static_cast<const F&>(f)() == -2);
+    assert(static_cast<F&&>(f)() == -2);
+    assert(static_cast<const F&&>(f)() == -2);
+  }
+
+  // Call to `not_fn<NTTP>` unspecified-type's operator() should always result in call to const& overload of .
+  {
+    { // Make sure unspecified-type is still callable when we delete & overload.
+      struct X {
+        FakeBool operator()() & = delete;
+        FakeBool operator()() const&;
+        FakeBool operator()() &&;
+        FakeBool operator()() const&&;
+      };
+
+      using F = decltype(std::not_fn<X{}>());
+      static_assert(std::invocable<F&>);
+      static_assert(std::invocable<const F&>);
+      static_assert(std::invocable<F>);
+      static_assert(std::invocable<const F>);
+    }
+
+    { // Make sure unspecified-type is not callable when we delete const& overload.
+      struct X {
+        FakeBool operator()() &;
+        FakeBool operator()() const& = delete;
+        FakeBool operator()() &&;
+        FakeBool operator()() const&&;
+      };
+
+      using F = decltype(std::not_fn<X{}>());
+      static_assert(!std::invocable<F&>);
+      static_assert(!std::invocable<const F&>);
+      static_assert(!std::invocable<F>);
+      static_assert(!std::invocable<const F>);
+    }
+
+    { // Make sure unspecified-type is still callable when we delete && overload.
+      struct X {
+        FakeBool operator()() &;
+        FakeBool operator()() const&;
+        FakeBool operator()() && = delete;
+        FakeBool operator()() const&&;
+      };
+
+      using F = decltype(std::not_fn<X{}>());
+      static_assert(std::invocable<F&>);
+      static_assert(std::invocable<const F&>);
+      static_assert(std::invocable<F>);
+      static_assert(std::invocable<const F>);
+    }
+
+    { // Make sure unspecified-type is still callable when we delete const&& overload.
+      struct X {
+        FakeBool operator()() &;
+        FakeBool operator()() const&;
+        FakeBool operator()() &&;
+        FakeBool operator()() const&& = delete;
+      };
+
+      using F = decltype(std::not_fn<X{}>());
+      static_assert(std::invocable<F&>);
+      static_assert(std::invocable<const F&>);
+      static_assert(std::invocable<F>);
+      static_assert(std::invocable<const F>);
+    }
+  }
+
+  { // Test perfect forwarding
+    auto f = [](int& val) {
+      val = 5;
+      return false;
+    };
+
+    auto not_f = std::not_fn<f>();
+    int val    = 0;
+    assert(not_f(val));
+    assert(val == 5);
+
+    using NotF = decltype(not_f);
+    static_assert(std::invocable<NotF, int&>);
+    static_assert(!std::invocable<NotF, int>);
+  }
+}
+
+constexpr void test_return_type() {
+  { // Test constructors and assignment operators
+    struct IsPowerOfTwo {
+      constexpr bool operator()(unsigned int x) const { return std::has_single_bit(x); }
+    };
+
+    auto is_not_power_of_2 = std::not_fn<IsPowerOfTwo{}>();
+    assert(is_not_power_of_2(5));
+    assert(!is_not_power_of_2(4));
+
+    auto moved = std::move(is_not_power_of_2);
+    assert(moved(5));
+    assert(!moved(4));
+
+    auto copied = is_not_power_of_2;
+    assert(copied(7));
+    assert(!copied(8));
+
+    moved = std::move(copied);
+    assert(copied(9));
+    assert(!copied(16));
+
+    copied = moved;
+    assert(copied(11));
+    assert(!copied(32));
+  }
+
+  { // Make sure `not_fn<NTTP>` unspecified type's operator() is SFINAE-friendly.
+    using F = decltype(std::not_fn<[](int x) { return !x; }>());
+    static_assert(!std::is_invocable<F>::value);
+    static_assert(std::is_invocable<F, int>::value);
+    static_assert(!std::is_invocable<F, void*>::value);
+    static_assert(!std::is_invocable<F, int, int>::value);
+  }
+
+  { // Test noexceptness
+    auto always_noexcept = std::not_fn<MaybeNoexceptFn<true>{}>();
+    static_assert(noexcept(always_noexcept()));
+
+    auto never_noexcept = std::not_fn<MaybeNoexceptFn<false>{}>();
+    static_assert(!noexcept(never_noexcept()));
+  }
+
+  { // Test calling volatile wrapper
+    using NotFn = decltype(std:...
[truncated]

Copy link

github-actions bot commented Mar 21, 2024

✅ With the latest revision this PR passed the Python code formatter.

template <auto _Fn>
struct __nttp_not_fn_t {
template <class... _Args>
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Args&&... __args) const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Args&&... __args) const
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Args&&... __args)

I think we can make this operator static?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

volatile values can be rejected by not using static, but this doesn't seem worthwhile... See [func.require]/4 and LWG4007.

Copy link
Contributor Author

@JMazurkiewicz JMazurkiewicz Mar 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially, I've made operator() static but due to the reason previously mentioned

volatile values can be rejected by not using static

I've made it member function again. For now, I'm going to leave it as it is and wait for maintainers' comment.

@JMazurkiewicz JMazurkiewicz force-pushed the libcxx/functional/nttp_not_fn branch 2 times, most recently from 99f59e4 to fc15d9f Compare May 14, 2024 14:50
Implement `not_fn<NTTP>` function from "P2714R1 Bind front and back to NTTP callables".
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants