382 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++2010 11// <functional>12 13// template<class F, class... Args>14// constexpr unspecified bind_back(F&& f, Args&&... args);15 16#include <functional>17 18#include <cassert>19#include <concepts>20#include <tuple>21#include <utility>22 23#include "callable_types.h"24#include "types.h"25 26constexpr void test_basic_bindings() {27 { // Bind arguments, call without arguments28 {29 auto f = std::bind_back(MakeTuple{});30 assert(f() == std::make_tuple());31 }32 {33 auto f = std::bind_back(MakeTuple{}, Elem<1>{});34 assert(f() == std::make_tuple(Elem<1>{}));35 }36 {37 auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{});38 assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}));39 }40 {41 auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{});42 assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}));43 }44 }45 46 { // Bind no arguments, call with arguments47 {48 auto f = std::bind_back(MakeTuple{});49 assert(f(Elem<1>{}) == std::make_tuple(Elem<1>{}));50 }51 {52 auto f = std::bind_back(MakeTuple{});53 assert(f(Elem<1>{}, Elem<2>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}));54 }55 {56 auto f = std::bind_back(MakeTuple{});57 assert(f(Elem<1>{}, Elem<2>{}, Elem<3>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}));58 }59 }60 61 { // Bind arguments, call with arguments62 {63 auto f = std::bind_back(MakeTuple{}, Elem<1>{});64 assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}));65 }66 {67 auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{});68 assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}, Elem<2>{}));69 }70 {71 auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{});72 assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}, Elem<2>{}, Elem<3>{}));73 }74 75 {76 auto f = std::bind_back(MakeTuple{}, Elem<1>{});77 assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}));78 }79 {80 auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{});81 assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}, Elem<2>{}));82 }83 {84 auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{});85 assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}, Elem<2>{}, Elem<3>{}));86 }87 {88 auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{});89 assert(f(Elem<10>{}, Elem<11>{}, Elem<12>{}) ==90 std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<12>{}, Elem<1>{}, Elem<2>{}, Elem<3>{}));91 }92 }93 94 { // Basic tests with fundamental types95 int n = 2;96 int m = 1;97 int sum = 0;98 auto add = [](int x, int y) { return x + y; };99 auto add_n = [](int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f; };100 auto add_ref = [&](int x, int y) -> int& { return sum = x + y; };101 auto add_rref = [&](int x, int y) -> int&& { return std::move(sum = x + y); };102 103 auto a = std::bind_back(add, m, n);104 assert(a() == 3);105 106 auto b = std::bind_back(add_n, m, n, m, m, m, m);107 assert(b() == 7);108 109 auto c = std::bind_back(add_n, n, m);110 assert(c(1, 1, 1, 1) == 7);111 112 auto d = std::bind_back(add_ref, n, m);113 std::same_as<int&> decltype(auto) dresult(d());114 assert(dresult == 3);115 116 auto e = std::bind_back(add_rref, n, m);117 std::same_as<int&&> decltype(auto) eresult(e());118 assert(eresult == 3);119 120 auto f = std::bind_back(add, n);121 assert(f(3) == 5);122 123 auto g = std::bind_back(add, n, 1);124 assert(g() == 3);125 126 auto h = std::bind_back(add_n, 1, 1, 1);127 assert(h(2, 2, 2) == 9);128 129 auto i = std::bind_back(add_ref, n);130 std::same_as<int&> decltype(auto) iresult(i(5));131 assert(iresult == 7);132 133 auto j = std::bind_back(add_rref, m);134 std::same_as<int&&> decltype(auto) jresult(j(4));135 assert(jresult == 5);136 }137}138 139constexpr void test_edge_cases() {140 { // Make sure we don't treat std::reference_wrapper specially.141 auto sub = [](std::reference_wrapper<int> a, std::reference_wrapper<int> b) { return a.get() - b.get(); };142 143 int i = 1;144 int j = 2;145 auto f = std::bind_back(sub, std::ref(i));146 assert(f(std::ref(j)) == 1);147 }148 149 { // Make sure we can call a function that's a pointer to a member function.150 struct MemberFunction {151 constexpr int foo(int x, int y) { return x * y; }152 };153 154 MemberFunction value;155 auto fn = std::bind_back(&MemberFunction::foo, 2, 3);156 assert(fn(value) == 6);157 }158 159 { // Make sure we can call a function that's a pointer to a member object.160 struct MemberObject {161 int obj;162 };163 164 MemberObject value{.obj = 3};165 auto fn = std::bind_back(&MemberObject::obj);166 assert(fn(value) == 3);167 }168}169 170constexpr void test_passing_arguments() {171 { // Make sure that we copy the bound arguments into the unspecified-type.172 auto add = [](int x, int y) { return x + y; };173 int n = 2;174 auto f = std::bind_back(add, n, 1);175 n = 100;176 assert(f() == 3);177 }178 179 { // Make sure we pass the bound arguments to the function object180 // with the right value category.181 {182 auto was_copied = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::copy; };183 CopyMoveInfo info;184 auto f = std::bind_back(was_copied, info);185 assert(f());186 }187 188 {189 auto was_moved = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::move; };190 CopyMoveInfo info;191 auto f = std::bind_back(was_moved, info);192 assert(std::move(f)());193 }194 }195}196 197constexpr void test_function_objects() {198 { // Make sure we call the correctly cv-ref qualified operator()199 // based on the value category of the bind_back unspecified-type.200 struct X {201 constexpr int operator()() & { return 1; }202 constexpr int operator()() const& { return 2; }203 constexpr int operator()() && { return 3; }204 constexpr int operator()() const&& { return 4; }205 };206 207 auto f = std::bind_back(X{});208 using F = decltype(f);209 assert(static_cast<F&>(f)() == 1);210 assert(static_cast<const F&>(f)() == 2);211 assert(static_cast<F&&>(f)() == 3);212 assert(static_cast<const F&&>(f)() == 4);213 }214 215 // Make sure the `bind_back` unspecified-type does not model invocable216 // when the call would select a differently-qualified operator().217 //218 // For example, if the call to `operator()() &` is ill-formed, the call to the unspecified-type219 // should be ill-formed and not fall back to the `operator()() const&` overload.220 { // Make sure we delete the & overload when the underlying call isn't valid.221 {222 struct X {223 void operator()() & = delete;224 void operator()() const&;225 void operator()() &&;226 void operator()() const&&;227 };228 229 using F = decltype(std::bind_back(X{}));230 static_assert(!std::invocable<F&>);231 static_assert(std::invocable<const F&>);232 static_assert(std::invocable<F>);233 static_assert(std::invocable<const F>);234 }235 236 // There's no way to make sure we delete the const& overload when the underlying call isn't valid,237 // so we can't check this one.238 239 { // Make sure we delete the && overload when the underlying call isn't valid.240 struct X {241 void operator()() &;242 void operator()() const&;243 void operator()() && = delete;244 void operator()() const&&;245 };246 247 using F = decltype(std::bind_back(X{}));248 static_assert(std::invocable<F&>);249 static_assert(std::invocable<const F&>);250 static_assert(!std::invocable<F>);251 static_assert(std::invocable<const F>);252 }253 254 { // Make sure we delete the const&& overload when the underlying call isn't valid.255 struct X {256 void operator()() &;257 void operator()() const&;258 void operator()() &&;259 void operator()() const&& = delete;260 };261 262 using F = decltype(std::bind_back(X{}));263 static_assert(std::invocable<F&>);264 static_assert(std::invocable<const F&>);265 static_assert(std::invocable<F>);266 static_assert(!std::invocable<const F>);267 }268 }269 270 { // Extra value category tests271 struct X {};272 273 {274 struct Y {275 void operator()(X&&) const&;276 void operator()(X&&) && = delete;277 };278 279 using F = decltype(std::bind_back(Y{}));280 static_assert(std::invocable<F&, X>);281 static_assert(!std::invocable<F, X>);282 }283 284 {285 struct Y {286 void operator()(const X&) const;287 void operator()(X&&) const = delete;288 };289 290 using F = decltype(std::bind_back(Y{}, X{}));291 static_assert(std::invocable<F&>);292 static_assert(!std::invocable<F>);293 }294 }295}296 297constexpr void test_return_type() {298 { // Test properties of the constructor of the unspecified-type returned by bind_back.299 { // Test move constructor when function is move only.300 MoveOnlyCallable<bool> value(true);301 auto f = std::bind_back(std::move(value), 1);302 assert(f());303 assert(f(1, 2, 3));304 305 auto f1 = std::move(f);306 assert(!f());307 assert(f1());308 assert(f1(1, 2, 3));309 310 using F = decltype(f);311 static_assert(std::is_move_constructible<F>::value);312 static_assert(!std::is_copy_constructible<F>::value);313 static_assert(!std::is_move_assignable<F>::value);314 static_assert(!std::is_copy_assignable<F>::value);315 }316 317 { // Test move constructor when function is copyable but not assignable.318 CopyCallable<bool> value(true);319 auto f = std::bind_back(value, 1);320 assert(f());321 assert(f(1, 2, 3));322 323 auto f1 = std::move(f);324 assert(!f());325 assert(f1());326 assert(f1(1, 2, 3));327 328 auto f2 = std::bind_back(std::move(value), 1);329 assert(f1());330 assert(f2());331 assert(f2(1, 2, 3));332 333 using F = decltype(f);334 static_assert(std::is_move_constructible<F>::value);335 static_assert(std::is_copy_constructible<F>::value);336 static_assert(!std::is_move_assignable<F>::value);337 static_assert(!std::is_copy_assignable<F>::value);338 }339 340 { // Test constructors when function is copy assignable.341 using F = decltype(std::bind_back(std::declval<CopyAssignableWrapper&>(), 1));342 static_assert(std::is_move_constructible<F>::value);343 static_assert(std::is_copy_constructible<F>::value);344 static_assert(std::is_move_assignable<F>::value);345 static_assert(std::is_copy_assignable<F>::value);346 }347 348 { // Test constructors when function is move assignable only.349 using F = decltype(std::bind_back(std::declval<MoveAssignableWrapper>(), 1));350 static_assert(std::is_move_constructible<F>::value);351 static_assert(!std::is_copy_constructible<F>::value);352 static_assert(std::is_move_assignable<F>::value);353 static_assert(!std::is_copy_assignable<F>::value);354 }355 }356 357 { // Make sure bind_back's unspecified type's operator() is SFINAE-friendly.358 using F = decltype(std::bind_back(std::declval<int (*)(int, int)>(), 1));359 static_assert(!std::is_invocable<F>::value);360 static_assert(std::is_invocable<F, int>::value);361 static_assert(!std::is_invocable<F, void*>::value);362 static_assert(!std::is_invocable<F, int, int>::value);363 }364}365 366constexpr bool test() {367 test_basic_bindings();368 test_edge_cases();369 test_passing_arguments();370 test_function_objects();371 test_return_type();372 373 return true;374}375 376int main(int, char**) {377 test();378 static_assert(test());379 380 return 0;381}382