| 1 | //===----------------------------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23 |
| 10 | |
| 11 | // <functional> |
| 12 | |
| 13 | // template<auto f> constexpr unspecified not_fn() noexcept; |
| 14 | |
| 15 | #include <functional> |
| 16 | |
| 17 | #include <bit> |
| 18 | #include <cassert> |
| 19 | #include <concepts> |
| 20 | #include <type_traits> |
| 21 | #include <utility> |
| 22 | |
| 23 | #include "test_macros.h" |
| 24 | |
| 25 | class BooleanTestable { |
| 26 | bool val_; |
| 27 | |
| 28 | public: |
| 29 | constexpr explicit BooleanTestable(bool val) : val_(val) {} |
| 30 | constexpr operator bool() const { return val_; } |
| 31 | constexpr BooleanTestable operator!() const { return BooleanTestable{!val_}; } |
| 32 | }; |
| 33 | |
| 34 | LIBCPP_STATIC_ASSERT(std::__boolean_testable<BooleanTestable>); |
| 35 | |
| 36 | class FakeBool { |
| 37 | int val_; |
| 38 | |
| 39 | public: |
| 40 | constexpr FakeBool(int val) : val_(val) {} |
| 41 | constexpr FakeBool operator!() const { return FakeBool{-val_}; } |
| 42 | constexpr bool operator==(int other) const { return val_ == other; } |
| 43 | }; |
| 44 | |
| 45 | template <bool IsNoexcept> |
| 46 | struct MaybeNoexceptFn { |
| 47 | bool operator()() const noexcept(IsNoexcept); // not defined |
| 48 | }; |
| 49 | |
| 50 | template <bool IsNoexcept> |
| 51 | struct MaybeNoexceptNegation { |
| 52 | bool operator!() noexcept(IsNoexcept); // not defined |
| 53 | }; |
| 54 | |
| 55 | template <bool IsNoexcept> |
| 56 | MaybeNoexceptNegation<IsNoexcept> maybe_noexcept_negation() noexcept { |
| 57 | return {}; |
| 58 | } |
| 59 | |
| 60 | constexpr void basic_tests() { |
| 61 | { // Test constant functions |
| 62 | auto false_fn = std::not_fn<std::false_type{}>(); |
| 63 | assert(false_fn()); |
| 64 | |
| 65 | auto true_fn = std::not_fn<std::true_type{}>(); |
| 66 | assert(!true_fn()); |
| 67 | |
| 68 | static_assert(noexcept(std::not_fn<std::false_type{}>())); |
| 69 | static_assert(noexcept(std::not_fn<std::true_type{}>())); |
| 70 | } |
| 71 | |
| 72 | { // Test function with one argument |
| 73 | auto is_odd = std::not_fn<[](auto x) { return x % 2 == 0; }>(); |
| 74 | assert(is_odd(1)); |
| 75 | assert(!is_odd(2)); |
| 76 | assert(is_odd(3)); |
| 77 | assert(!is_odd(4)); |
| 78 | assert(is_odd(5)); |
| 79 | } |
| 80 | |
| 81 | { // Test function with multiple arguments |
| 82 | auto at_least_10 = [](auto... vals) { return (vals + ... + 0) >= 10; }; |
| 83 | auto at_most_9 = std::not_fn<at_least_10>(); |
| 84 | assert(at_most_9()); |
| 85 | assert(at_most_9(1)); |
| 86 | assert(at_most_9(1, 2, 3, 4, -1)); |
| 87 | assert(at_most_9(3, 3, 2, 1, -2)); |
| 88 | assert(!at_most_9(10, -1, 2)); |
| 89 | assert(!at_most_9(5, 5)); |
| 90 | static_assert(noexcept(std::not_fn<at_least_10>())); |
| 91 | } |
| 92 | |
| 93 | { // Test function that returns boolean-testable type other than bool |
| 94 | auto is_product_even = [](auto... vals) { return BooleanTestable{(vals * ... * 1) % 2 == 0}; }; |
| 95 | auto is_product_odd = std::not_fn<is_product_even>(); |
| 96 | assert(is_product_odd()); |
| 97 | assert(is_product_odd(1, 3, 5, 9)); |
| 98 | assert(is_product_odd(3, 3, 3, 3)); |
| 99 | assert(!is_product_odd(3, 5, 9, 11, 0)); |
| 100 | assert(!is_product_odd(11, 7, 5, 3, 2)); |
| 101 | static_assert(noexcept(std::not_fn<is_product_even>())); |
| 102 | } |
| 103 | |
| 104 | { // Test function that returns non-boolean-testable type |
| 105 | auto sum = [](auto... vals) -> FakeBool { return (vals + ... + 0); }; |
| 106 | auto negated_sum = std::not_fn<sum>(); |
| 107 | assert(negated_sum() == 0); |
| 108 | assert(negated_sum(3) == -3); |
| 109 | assert(negated_sum(4, 5, 1, 3) == -13); |
| 110 | assert(negated_sum(4, 2, 5, 6, 1) == -18); |
| 111 | assert(negated_sum(-1, 3, 2, -8) == 4); |
| 112 | static_assert(noexcept(std::not_fn<sum>())); |
| 113 | } |
| 114 | |
| 115 | { // Test member pointers |
| 116 | struct MemberPointerTester { |
| 117 | bool value = true; |
| 118 | constexpr bool not_value() const { return !value; } |
| 119 | constexpr bool value_and(bool other) noexcept { return value && other; } |
| 120 | }; |
| 121 | |
| 122 | MemberPointerTester tester; |
| 123 | |
| 124 | auto not_mem_object = std::not_fn<&MemberPointerTester::value>(); |
| 125 | assert(!not_mem_object(tester)); |
| 126 | assert(!not_mem_object(std::as_const(tester))); |
| 127 | static_assert(noexcept(not_mem_object(tester))); |
| 128 | static_assert(noexcept(not_mem_object(std::as_const(t&: tester)))); |
| 129 | |
| 130 | auto not_nullary_mem_fn = std::not_fn<&MemberPointerTester::not_value>(); |
| 131 | assert(not_nullary_mem_fn(tester)); |
| 132 | static_assert(!noexcept(not_nullary_mem_fn(tester))); |
| 133 | |
| 134 | auto not_unary_mem_fn = std::not_fn<&MemberPointerTester::value_and>(); |
| 135 | assert(not_unary_mem_fn(tester, false)); |
| 136 | static_assert(noexcept(not_unary_mem_fn(tester, false))); |
| 137 | static_assert(!std::is_invocable_v<decltype(not_unary_mem_fn), const MemberPointerTester&, bool>); |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | constexpr void test_perfect_forwarding_call_wrapper() { |
| 142 | { // Make sure we call the correctly cv-ref qualified operator() |
| 143 | // based on the value category of the not_fn<NTTP> unspecified-type. |
| 144 | struct X { |
| 145 | constexpr FakeBool operator()() & { return 1; } |
| 146 | constexpr FakeBool operator()() const& { return 2; } |
| 147 | constexpr FakeBool operator()() && { return 3; } |
| 148 | constexpr FakeBool operator()() const&& { return 4; } |
| 149 | }; |
| 150 | |
| 151 | auto f = std::not_fn<X{}>(); |
| 152 | using F = decltype(f); |
| 153 | assert(static_cast<F&>(f)() == -2); |
| 154 | assert(static_cast<const F&>(f)() == -2); |
| 155 | assert(static_cast<F&&>(f)() == -2); |
| 156 | assert(static_cast<const F&&>(f)() == -2); |
| 157 | } |
| 158 | |
| 159 | // Call to `not_fn<NTTP>` unspecified-type's operator() should always result in call to the const& overload of the underlying function object. |
| 160 | { |
| 161 | { // Make sure unspecified-type is still callable when we delete the & overload. |
| 162 | struct X { |
| 163 | FakeBool operator()() & = delete; |
| 164 | FakeBool operator()() const&; |
| 165 | FakeBool operator()() &&; |
| 166 | FakeBool operator()() const&&; |
| 167 | }; |
| 168 | |
| 169 | using F = decltype(std::not_fn<X{}>()); |
| 170 | static_assert(std::invocable<F&>); |
| 171 | static_assert(std::invocable<const F&>); |
| 172 | static_assert(std::invocable<F>); |
| 173 | static_assert(std::invocable<const F>); |
| 174 | } |
| 175 | |
| 176 | { // Make sure unspecified-type is not callable when we delete the const& overload. |
| 177 | struct X { |
| 178 | FakeBool operator()() &; |
| 179 | FakeBool operator()() const& = delete; |
| 180 | FakeBool operator()() &&; |
| 181 | FakeBool operator()() const&&; |
| 182 | }; |
| 183 | |
| 184 | using F = decltype(std::not_fn<X{}>()); |
| 185 | static_assert(!std::invocable<F&>); |
| 186 | static_assert(!std::invocable<const F&>); |
| 187 | static_assert(!std::invocable<F>); |
| 188 | static_assert(!std::invocable<const F>); |
| 189 | } |
| 190 | |
| 191 | { // Make sure unspecified-type is still callable when we delete the && overload. |
| 192 | struct X { |
| 193 | FakeBool operator()() &; |
| 194 | FakeBool operator()() const&; |
| 195 | FakeBool operator()() && = delete; |
| 196 | FakeBool operator()() const&&; |
| 197 | }; |
| 198 | |
| 199 | using F = decltype(std::not_fn<X{}>()); |
| 200 | static_assert(std::invocable<F&>); |
| 201 | static_assert(std::invocable<const F&>); |
| 202 | static_assert(std::invocable<F>); |
| 203 | static_assert(std::invocable<const F>); |
| 204 | } |
| 205 | |
| 206 | { // Make sure unspecified-type is still callable when we delete the const&& overload. |
| 207 | struct X { |
| 208 | FakeBool operator()() &; |
| 209 | FakeBool operator()() const&; |
| 210 | FakeBool operator()() &&; |
| 211 | FakeBool operator()() const&& = delete; |
| 212 | }; |
| 213 | |
| 214 | using F = decltype(std::not_fn<X{}>()); |
| 215 | static_assert(std::invocable<F&>); |
| 216 | static_assert(std::invocable<const F&>); |
| 217 | static_assert(std::invocable<F>); |
| 218 | static_assert(std::invocable<const F>); |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | { // Test perfect forwarding |
| 223 | auto f = [](int& val) { |
| 224 | val = 5; |
| 225 | return false; |
| 226 | }; |
| 227 | |
| 228 | auto not_f = std::not_fn<f>(); |
| 229 | int val = 0; |
| 230 | assert(not_f(val)); |
| 231 | assert(val == 5); |
| 232 | |
| 233 | using NotF = decltype(not_f); |
| 234 | static_assert(std::invocable<NotF, int&>); |
| 235 | static_assert(!std::invocable<NotF, int>); |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | constexpr void test_return_type() { |
| 240 | { // Test constructors and assignment operators |
| 241 | struct IsPowerOfTwo { |
| 242 | constexpr bool operator()(unsigned int x) const { return std::has_single_bit(x: x); } |
| 243 | }; |
| 244 | |
| 245 | auto is_not_power_of_2 = std::not_fn<IsPowerOfTwo{}>(); |
| 246 | assert(is_not_power_of_2(5)); |
| 247 | assert(!is_not_power_of_2(4)); |
| 248 | |
| 249 | auto moved = std::move(is_not_power_of_2); |
| 250 | assert(moved(5)); |
| 251 | assert(!moved(4)); |
| 252 | |
| 253 | auto copied = is_not_power_of_2; |
| 254 | assert(copied(7)); |
| 255 | assert(!copied(8)); |
| 256 | |
| 257 | moved = std::move(copied); |
| 258 | assert(copied(9)); |
| 259 | assert(!copied(16)); |
| 260 | |
| 261 | copied = moved; |
| 262 | assert(copied(11)); |
| 263 | assert(!copied(32)); |
| 264 | } |
| 265 | |
| 266 | { // Make sure `not_fn<NTTP>` unspecified-type's operator() is SFINAE-friendly. |
| 267 | using F = decltype(std::not_fn<[](int x) { return !x; }>()); |
| 268 | static_assert(!std::is_invocable<F>::value); |
| 269 | static_assert(std::is_invocable<F, int>::value); |
| 270 | static_assert(!std::is_invocable<F, void*>::value); |
| 271 | static_assert(!std::is_invocable<F, int, int>::value); |
| 272 | } |
| 273 | |
| 274 | { // Test noexceptness |
| 275 | auto always_noexcept = std::not_fn<MaybeNoexceptFn<true>{}>(); |
| 276 | static_assert(noexcept(always_noexcept())); |
| 277 | |
| 278 | auto never_noexcept = std::not_fn<MaybeNoexceptFn<false>{}>(); |
| 279 | static_assert(!noexcept(never_noexcept())); |
| 280 | |
| 281 | auto always_noexcept_negation = std::not_fn<maybe_noexcept_negation<true>>(); |
| 282 | static_assert(noexcept(always_noexcept_negation())); |
| 283 | |
| 284 | auto never_noexcept_negation = std::not_fn<maybe_noexcept_negation<false>>(); |
| 285 | static_assert(!noexcept(never_noexcept_negation())); |
| 286 | } |
| 287 | |
| 288 | { // Test calling volatile wrapper |
| 289 | using NotFn = decltype(std::not_fn<std::false_type{}>()); |
| 290 | static_assert(!std::invocable<volatile NotFn&>); |
| 291 | static_assert(!std::invocable<const volatile NotFn&>); |
| 292 | static_assert(!std::invocable<volatile NotFn>); |
| 293 | static_assert(!std::invocable<const volatile NotFn>); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | constexpr bool test() { |
| 298 | basic_tests(); |
| 299 | test_perfect_forwarding_call_wrapper(); |
| 300 | test_return_type(); |
| 301 | |
| 302 | return true; |
| 303 | } |
| 304 | |
| 305 | int main(int, char**) { |
| 306 | test(); |
| 307 | static_assert(test()); |
| 308 | |
| 309 | return 0; |
| 310 | } |
| 311 | |