311 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++2310 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 25class BooleanTestable {26 bool val_;27 28public: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 34LIBCPP_STATIC_ASSERT(std::__boolean_testable<BooleanTestable>);35 36class FakeBool {37 int val_;38 39public: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 45template <bool IsNoexcept>46struct MaybeNoexceptFn {47 bool operator()() const noexcept(IsNoexcept); // not defined48};49 50template <bool IsNoexcept>51struct MaybeNoexceptNegation {52 bool operator!() noexcept(IsNoexcept); // not defined53};54 55template <bool IsNoexcept>56MaybeNoexceptNegation<IsNoexcept> maybe_noexcept_negation() noexcept {57 return {};58}59 60constexpr void basic_tests() {61 { // Test constant functions62 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 argument73 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 arguments82 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 bool94 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 type105 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 pointers116 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(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 141constexpr 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 forwarding223 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 239constexpr void test_return_type() {240 { // Test constructors and assignment operators241 struct IsPowerOfTwo {242 constexpr bool operator()(unsigned int x) const { return std::has_single_bit(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 noexceptness275 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 wrapper289 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 297constexpr bool test() {298 basic_tests();299 test_perfect_forwarding_call_wrapper();300 test_return_type();301 302 return true;303}304 305int main(int, char**) {306 test();307 static_assert(test());308 309 return 0;310}311