diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -108,10 +108,12 @@ __functional_base __functional/binary_function.h __functional/binary_negate.h + __functional/bind_back.h __functional/bind_front.h __functional/bind.h __functional/binder1st.h __functional/binder2nd.h + __functional/compose.h __functional/default_searcher.h __functional/function.h __functional/hash.h diff --git a/libcxx/include/__functional/bind_back.h b/libcxx/include/__functional/bind_back.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__functional/bind_back.h @@ -0,0 +1,65 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FUNCTIONAL_BIND_BACK_H +#define _LIBCPP___FUNCTIONAL_BIND_BACK_H + +#include <__config> +#include <__functional/invoke.h> +#include <__functional/perfect_forward.h> +#include <__utility/forward.h> +#include <__utility/integer_sequence.h> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +template > +struct __bind_back_op; + +template +struct __bind_back_op<_NBound, index_sequence<_Ip...>> { + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Fn&& __f, _Bound&& __bound, _Args&& ...__args) const + noexcept(noexcept(_VSTD::invoke(_VSTD::forward<_Fn>(__f), _VSTD::forward<_Args>(__args)..., _VSTD::get<_Ip>(_VSTD::forward<_Bound>(__bound))...))) + -> decltype( _VSTD::invoke(_VSTD::forward<_Fn>(__f), _VSTD::forward<_Args>(__args)..., _VSTD::get<_Ip>(_VSTD::forward<_Bound>(__bound))...)) + { return _VSTD::invoke(_VSTD::forward<_Fn>(__f), _VSTD::forward<_Args>(__args)..., _VSTD::get<_Ip>(_VSTD::forward<_Bound>(__bound))...); } +}; + +template +struct __bind_back_t : __perfect_forward<__bind_back_op>, _Fn, _BoundArgs> { + using __perfect_forward<__bind_back_op>, _Fn, _BoundArgs>::__perfect_forward; +}; + +template , _Fn>, + is_move_constructible>, + is_constructible, _Args>..., + is_move_constructible>... + >::value +>> +_LIBCPP_HIDE_FROM_ABI +constexpr auto __bind_back(_Fn&& __f, _Args&&... __args) + noexcept(noexcept(__bind_back_t, tuple...>>(_VSTD::forward<_Fn>(__f), _VSTD::forward_as_tuple(_VSTD::forward<_Args>(__args)...)))) + -> decltype( __bind_back_t, tuple...>>(_VSTD::forward<_Fn>(__f), _VSTD::forward_as_tuple(_VSTD::forward<_Args>(__args)...))) + { return __bind_back_t, tuple...>>(_VSTD::forward<_Fn>(__f), _VSTD::forward_as_tuple(_VSTD::forward<_Args>(__args)...)); } + +#endif // _LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___FUNCTIONAL_BIND_BACK_H diff --git a/libcxx/include/__functional/compose.h b/libcxx/include/__functional/compose.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__functional/compose.h @@ -0,0 +1,52 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FUNCTIONAL_COMPOSE_H +#define _LIBCPP___FUNCTIONAL_COMPOSE_H + +#include <__config> +#include <__functional/invoke.h> +#include <__functional/perfect_forward.h> +#include <__utility/forward.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +struct __compose_op { + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Fn1&& __f1, _Fn2&& __f2, _Args&&... __args) const + noexcept(noexcept(_VSTD::invoke(_VSTD::forward<_Fn1>(__f1), _VSTD::invoke(_VSTD::forward<_Fn2>(__f2), _VSTD::forward<_Args>(__args)...)))) + -> decltype( _VSTD::invoke(_VSTD::forward<_Fn1>(__f1), _VSTD::invoke(_VSTD::forward<_Fn2>(__f2), _VSTD::forward<_Args>(__args)...))) + { return _VSTD::invoke(_VSTD::forward<_Fn1>(__f1), _VSTD::invoke(_VSTD::forward<_Fn2>(__f2), _VSTD::forward<_Args>(__args)...)); } +}; + +template +struct __compose_t : __perfect_forward<__compose_op, _Fn1, _Fn2> { + using __perfect_forward<__compose_op, _Fn1, _Fn2>::__perfect_forward; +}; + +template +_LIBCPP_HIDE_FROM_ABI +constexpr auto __compose(_Fn1&& __f1, _Fn2&& __f2) + noexcept(noexcept(__compose_t, decay_t<_Fn2>>(_VSTD::forward<_Fn1>(__f1), _VSTD::forward<_Fn2>(__f2)))) + -> decltype( __compose_t, decay_t<_Fn2>>(_VSTD::forward<_Fn1>(__f1), _VSTD::forward<_Fn2>(__f2))) + { return __compose_t, decay_t<_Fn2>>(_VSTD::forward<_Fn1>(__f1), _VSTD::forward<_Fn2>(__f2)); } + +#endif // _LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___FUNCTIONAL_COMPOSE_H diff --git a/libcxx/include/functional b/libcxx/include/functional --- a/libcxx/include/functional +++ b/libcxx/include/functional @@ -492,10 +492,12 @@ #include <__debug> #include <__functional/binary_function.h> // TODO: deprecate #include <__functional/binary_negate.h> +#include <__functional/bind_back.h> #include <__functional/bind_front.h> #include <__functional/bind.h> #include <__functional/binder1st.h> #include <__functional/binder2nd.h> +#include <__functional/compose.h> #include <__functional/default_searcher.h> #include <__functional/function.h> #include <__functional/hash.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -419,15 +419,17 @@ module binary_function { private header "__functional/binary_function.h" } module binary_negate { private header "__functional/binary_negate.h" } module bind { private header "__functional/bind.h" } + module bind_back { private header "__functional/bind_back.h" } module bind_front { private header "__functional/bind_front.h" } module binder1st { private header "__functional/binder1st.h" } module binder2nd { private header "__functional/binder2nd.h" } + module compose { private header "__functional/compose.h" } module default_searcher { private header "__functional/default_searcher.h" } module function { private header "__functional/function.h" } module hash { private header "__functional/hash.h" } module identity { private header "__functional/identity.h" } - module is_transparent { private header "__functional/is_transparent.h" } module invoke { private header "__functional/invoke.h" } + module is_transparent { private header "__functional/is_transparent.h" } module mem_fn { private header "__functional/mem_fn.h" } module mem_fun_ref { private header "__functional/mem_fun_ref.h" } module not_fn { private header "__functional/not_fn.h" } diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/functional/bind_back.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/functional/bind_back.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/functional/bind_back.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__functional/bind_back.h'}} +#include <__functional/bind_back.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/functional/compose.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/functional/compose.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/functional/compose.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__functional/compose.h'}} +#include <__functional/compose.h> diff --git a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.bind.partial/bind_back.pass.cpp copy from libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp copy to libcxx/test/libcxx/utilities/function.objects/func.bind.partial/bind_back.pass.cpp --- a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp +++ b/libcxx/test/libcxx/utilities/function.objects/func.bind.partial/bind_back.pass.cpp @@ -11,12 +11,15 @@ // functional // template -// constexpr unspecified bind_front(F&&, Args&&...); +// constexpr unspecified __bind_back(F&&, Args&&...); + +// This isn't part of the standard, however we use it internally and there is a +// chance that it will be added to the standard, so we implement those tests +// as-if it were part of the spec. #include #include #include -#include #include #include "callable_types.h" @@ -31,10 +34,10 @@ }; template -struct is_bind_frontable { +struct is_bind_backable { template static auto test(int) - -> decltype((void)std::bind_front(std::declval()...), std::true_type()); + -> decltype((void)std::__bind_back(std::declval()...), std::true_type()); template static std::false_type test(...); @@ -78,19 +81,19 @@ // Bind arguments, call without arguments { { - auto f = std::bind_front(MakeTuple{}); + auto f = std::__bind_back(MakeTuple{}); assert(f() == std::make_tuple()); } { - auto f = std::bind_front(MakeTuple{}, Elem<1>{}); + auto f = std::__bind_back(MakeTuple{}, Elem<1>{}); assert(f() == std::make_tuple(Elem<1>{})); } { - auto f = std::bind_front(MakeTuple{}, Elem<1>{}, Elem<2>{}); + auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}); assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{})); } { - auto f = std::bind_front(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{}); + auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{}); assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{})); } } @@ -98,15 +101,15 @@ // Bind no arguments, call with arguments { { - auto f = std::bind_front(MakeTuple{}); + auto f = std::__bind_back(MakeTuple{}); assert(f(Elem<1>{}) == std::make_tuple(Elem<1>{})); } { - auto f = std::bind_front(MakeTuple{}); + auto f = std::__bind_back(MakeTuple{}); assert(f(Elem<1>{}, Elem<2>{}) == std::make_tuple(Elem<1>{}, Elem<2>{})); } { - auto f = std::bind_front(MakeTuple{}); + auto f = std::__bind_back(MakeTuple{}); assert(f(Elem<1>{}, Elem<2>{}, Elem<3>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{})); } } @@ -114,29 +117,29 @@ // Bind arguments, call with arguments { { - auto f = std::bind_front(MakeTuple{}, Elem<1>{}); - assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<10>{})); + auto f = std::__bind_back(MakeTuple{}, Elem<1>{}); + assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{})); } { - auto f = std::bind_front(MakeTuple{}, Elem<1>{}, Elem<2>{}); - assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<10>{})); + auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}); + assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}, Elem<2>{})); } { - auto f = std::bind_front(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{}); - assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}, Elem<10>{})); + auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{}); + assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}, Elem<2>{}, Elem<3>{})); } { - auto f = std::bind_front(MakeTuple{}, Elem<1>{}); - assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<10>{}, Elem<11>{})); + auto f = std::__bind_back(MakeTuple{}, Elem<1>{}); + assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{})); } { - auto f = std::bind_front(MakeTuple{}, Elem<1>{}, Elem<2>{}); - assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<10>{}, Elem<11>{})); + auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}); + assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}, Elem<2>{})); } { - auto f = std::bind_front(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{}); - assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}, Elem<10>{}, Elem<11>{})); + auto f = std::__bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{}); + assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}, Elem<2>{}, Elem<3>{})); } } @@ -149,33 +152,33 @@ return a + b + c + d + e + f; }; - auto a = std::bind_front(add, m, n); + auto a = std::__bind_back(add, m, n); assert(a() == 3); - auto b = std::bind_front(addN, m, n, m, m, m, m); + auto b = std::__bind_back(addN, m, n, m, m, m, m); assert(b() == 7); - auto c = std::bind_front(addN, n, m); + auto c = std::__bind_back(addN, n, m); assert(c(1, 1, 1, 1) == 7); - auto f = std::bind_front(add, n); + auto f = std::__bind_back(add, n); assert(f(3) == 5); - auto g = std::bind_front(add, n, 1); + auto g = std::__bind_back(add, n, 1); assert(g() == 3); - auto h = std::bind_front(addN, 1, 1, 1); + auto h = std::__bind_back(addN, 1, 1, 1); assert(h(2, 2, 2) == 9); } // Make sure we don't treat std::reference_wrapper specially. { - auto add = [](std::reference_wrapper a, std::reference_wrapper b) { - return a.get() + b.get(); + auto sub = [](std::reference_wrapper a, std::reference_wrapper b) { + return a.get() - b.get(); }; int i = 1, j = 2; - auto f = std::bind_front(add, std::ref(i)); - assert(f(std::ref(j)) == 3); + auto f = std::__bind_back(sub, std::ref(i)); + assert(f(std::ref(j)) == 2 - 1); } // Make sure we can call a function that's a pointer to a member function. @@ -184,15 +187,15 @@ constexpr bool foo(int, int) { return true; } }; MemberFunction value; - auto fn = std::bind_front(&MemberFunction::foo, value, 0); - assert(fn(0)); + auto fn = std::__bind_back(&MemberFunction::foo, 0, 0); + assert(fn(value)); } // Make sure that we copy the bound arguments into the unspecified-type. { auto add = [](int x, int y) { return x + y; }; int n = 2; - auto i = std::bind_front(add, n, 1); + auto i = std::__bind_back(add, n, 1); n = 100; assert(i() == 3); } @@ -205,7 +208,7 @@ return info.copy_kind == CopyMoveInfo::copy; }; CopyMoveInfo info; - auto copied = std::bind_front(wasCopied, info); + auto copied = std::__bind_back(wasCopied, info); assert(copied()); } @@ -214,13 +217,13 @@ return info.copy_kind == CopyMoveInfo::move; }; CopyMoveInfo info; - auto moved = std::bind_front(wasMoved, info); + auto moved = std::__bind_back(wasMoved, info); assert(std::move(moved)()); } } // Make sure we call the correctly cv-ref qualified operator() based on the - // value category of the bind_front unspecified-type. + // value category of the __bind_back unspecified-type. { struct F { constexpr int operator()() & { return 1; } @@ -228,7 +231,7 @@ constexpr int operator()() && { return 3; } constexpr int operator()() const&& { return 4; } }; - auto x = std::bind_front(F{}); + auto x = std::__bind_back(F{}); using X = decltype(x); assert(static_cast(x)() == 1); assert(static_cast(x)() == 2); @@ -236,7 +239,7 @@ assert(static_cast(x)() == 4); } - // Make sure the bind_front unspecified-type is NOT invocable when the call would select a + // Make sure the __bind_back unspecified-type is NOT invocable when the call would select a // differently-qualified operator(). // // For example, if the call to `operator()() &` is ill-formed, the call to the unspecified-type @@ -250,7 +253,7 @@ void operator()() &&; void operator()() const&&; }; - using X = decltype(std::bind_front(F{})); + using X = decltype(std::__bind_back(F{})); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); @@ -268,7 +271,7 @@ void operator()() && = delete; void operator()() const&&; }; - using X = decltype(std::bind_front(F{})); + using X = decltype(std::__bind_back(F{})); static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); @@ -283,7 +286,7 @@ void operator()() &&; void operator()() const&& = delete; }; - using X = decltype(std::bind_front(F{})); + using X = decltype(std::__bind_back(F{})); static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); @@ -299,7 +302,7 @@ void operator()(T&&) const &; void operator()(T&&) && = delete; }; - using X = decltype(std::bind_front(F{})); + using X = decltype(std::__bind_back(F{})); static_assert(!std::is_invocable_v); } @@ -309,16 +312,16 @@ void operator()(T const&) const; void operator()(T&&) const = delete; }; - using X = decltype(std::bind_front(F{}, T{})); + using X = decltype(std::__bind_back(F{}, T{})); static_assert(!std::is_invocable_v); } } - // Test properties of the constructor of the unspecified-type returned by bind_front. + // Test properties of the constructor of the unspecified-type returned by __bind_back. { { MoveOnlyCallable value(true); - auto ret = std::bind_front(std::move(value), 1); + auto ret = std::__bind_back(std::move(value), 1); assert(ret()); assert(ret(1, 2, 3)); @@ -335,7 +338,7 @@ } { CopyCallable value(true); - auto ret = std::bind_front(value, 1); + auto ret = std::__bind_back(value, 1); assert(ret()); assert(ret(1, 2, 3)); @@ -343,7 +346,7 @@ assert(ret1()); assert(ret1(1, 2, 3)); - auto ret2 = std::bind_front(std::move(value), 1); + auto ret2 = std::__bind_back(std::move(value), 1); assert(!ret()); assert(ret2()); assert(ret2(1, 2, 3)); @@ -356,7 +359,7 @@ } { CopyAssignableWrapper value(true); - using RetT = decltype(std::bind_front(value, 1)); + using RetT = decltype(std::__bind_back(value, 1)); static_assert(std::is_move_constructible::value); static_assert(std::is_copy_constructible::value); @@ -365,7 +368,7 @@ } { MoveAssignableWrapper value(true); - using RetT = decltype(std::bind_front(std::move(value), 1)); + using RetT = decltype(std::__bind_back(std::move(value), 1)); static_assert( std::is_move_constructible::value); static_assert(!std::is_copy_constructible::value); @@ -374,29 +377,32 @@ } } - // Make sure bind_front is SFINAE friendly + // Make sure __bind_back is SFINAE friendly { - using T = decltype(std::bind_front(std::declval(), 1)); - static_assert(!std::is_invocable::value); - static_assert( std::is_invocable::value); - static_assert(!std::is_invocable::value); - static_assert(!std::is_invocable::value); - static_assert(!std::is_constructible_v); static_assert(!std::is_move_constructible_v); - static_assert(!is_bind_frontable::value); - static_assert(!is_bind_frontable::value); + static_assert(!is_bind_backable::value); + static_assert(!is_bind_backable::value); auto takeAnything = [](auto&& ...) { }; static_assert(!std::is_constructible_v); static_assert( std::is_move_constructible_v); - static_assert( is_bind_frontable::value); - static_assert(!is_bind_frontable::value); + static_assert( is_bind_backable::value); + static_assert(!is_bind_backable::value); static_assert( std::is_constructible_v); static_assert(!std::is_move_constructible_v); - static_assert(!is_bind_frontable::value); - static_assert(!is_bind_frontable::value); + static_assert(!is_bind_backable::value); + static_assert(!is_bind_backable::value); + } + + // Make sure bind_back's unspecified type's operator() is SFINAE-friendly + { + using T = decltype(std::__bind_back(std::declval(), 1)); + static_assert(!std::is_invocable::value); + static_assert( std::is_invocable::value); + static_assert(!std::is_invocable::value); + static_assert(!std::is_invocable::value); } return true; diff --git a/libcxx/test/libcxx/utilities/function.objects/func.bind.partial/compose.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.bind.partial/compose.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/function.objects/func.bind.partial/compose.pass.cpp @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template +// constexpr unspecified __compose(F1&&, F2&&); + +#include +#include +#include +#include + +template +struct Elem { + template + constexpr bool operator==(Elem const&) const + { return X == Y; } +}; + +struct F { + template + constexpr auto operator()(Args&& ...args) const { + return std::make_tuple(Elem<888>{}, std::forward(args)...); + } +}; + +struct G { + template + constexpr auto operator()(Args&& ...args) const { + return std::make_tuple(Elem<999>{}, std::forward(args)...); + } +}; + +constexpr bool test() { + F const f; + G const g; + + { + auto c = std::__compose(f, g); + assert(c() == f(g())); + } + { + auto c = std::__compose(f, g); + assert(c(Elem<0>{}) == f(g(Elem<0>{}))); + } + { + auto c = std::__compose(f, g); + assert(c(Elem<0>{}, Elem<1>{}) == f(g(Elem<0>{}, Elem<1>{}))); + } + { + auto c = std::__compose(f, g); + assert(c(Elem<0>{}, Elem<1>{}, Elem<2>{}) == f(g(Elem<0>{}, Elem<1>{}, Elem<2>{}))); + } + + // Make sure we can call a function that's a pointer to a member function. + { + struct MemberFunction1 { + constexpr Elem<0> foo() { return {}; } + }; + struct MemberFunction2 { + constexpr MemberFunction1 bar() { return {}; } + }; + auto c = std::__compose(&MemberFunction1::foo, &MemberFunction2::bar); + assert(c(MemberFunction2{}) == Elem<0>{}); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp b/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp --- a/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp +++ b/libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.pass.cpp @@ -376,12 +376,6 @@ // Make sure bind_front is SFINAE friendly { - using T = decltype(std::bind_front(std::declval(), 1)); - static_assert(!std::is_invocable::value); - static_assert( std::is_invocable::value); - static_assert(!std::is_invocable::value); - static_assert(!std::is_invocable::value); - static_assert(!std::is_constructible_v); static_assert(!std::is_move_constructible_v); static_assert(!is_bind_frontable::value); @@ -399,6 +393,15 @@ static_assert(!is_bind_frontable::value); } + // Make sure bind_front's unspecified type's operator() is SFINAE-friendly + { + using T = decltype(std::bind_front(std::declval(), 1)); + static_assert(!std::is_invocable::value); + static_assert( std::is_invocable::value); + static_assert(!std::is_invocable::value); + static_assert(!std::is_invocable::value); + } + return true; }