This is a snapshot of work in progress, for reference. bind_front<f>(...) is uglified directly from the sample implementation in P2714, at include/std/functional:1284 .
Test failures: bind_front/1.cc:53: error: static assertion failed bind_front/1.cc:57: error: static assertion failed bind_front/1.cc:214: error: static assertion failed bind_front/1.cc:215: error: static assertion failed bind_front/1.cc:216: required from here functional:1301: error: invalid conversion from 'std::invoke_result_t<const test03()::F&, std::reference_wrapper<int>&, void*&>' {aka 'void*'} to 'int' [-fpermissive] [... etc. ] Also complains about 218, 220, 231, 233-6, 264, 267 libstdc++-v3/ChangeLog: PR libstdc++/119744 * include/bits/version.def: Redefine __cpp_lib_bind_front etc. * include/bits/version.h: Ditto. * include/std/functional: Add new bind_front etc. overloads * testsuite/20_util/function_objects/bind_front/1.cc --- libstdc++-v3/include/bits/version.def | 12 ++ libstdc++-v3/include/bits/version.h | 21 ++- libstdc++-v3/include/std/functional | 124 +++++++++++++++++- .../20_util/function_objects/bind_front/1.cc | 103 ++++++++++++++- 4 files changed, 278 insertions(+), 5 deletions(-) diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 5d5758bf203..8ab9a7207e7 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -463,6 +463,10 @@ ftms = { ftms = { name = not_fn; + values = { + v = 202306; + cxxmin = 26; + }; values = { v = 201603; cxxmin = 17; @@ -776,6 +780,10 @@ ftms = { ftms = { name = bind_front; + values = { + v = 202306; + cxxmin = 26; + }; values = { v = 201907; cxxmin = 20; @@ -784,6 +792,10 @@ ftms = { ftms = { name = bind_back; + values = { + v = 202306; + cxxmin = 26; + }; values = { v = 202202; cxxmin = 23; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 2b00e8419b3..c204ae3c48c 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -511,7 +511,12 @@ #undef __glibcxx_want_make_from_tuple #if !defined(__cpp_lib_not_fn) -# if (__cplusplus >= 201703L) +# if (__cplusplus > 202302L) +# define __glibcxx_not_fn 202306L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_not_fn) +# define __cpp_lib_not_fn 202306L +# endif +# elif (__cplusplus >= 201703L) # define __glibcxx_not_fn 201603L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_not_fn) # define __cpp_lib_not_fn 201603L @@ -866,7 +871,12 @@ #undef __glibcxx_want_atomic_value_initialization #if !defined(__cpp_lib_bind_front) -# if (__cplusplus >= 202002L) +# if (__cplusplus > 202302L) +# define __glibcxx_bind_front 202306L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_bind_front) +# define __cpp_lib_bind_front 202306L +# endif +# elif (__cplusplus >= 202002L) # define __glibcxx_bind_front 201907L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_bind_front) # define __cpp_lib_bind_front 201907L @@ -876,7 +886,12 @@ #undef __glibcxx_want_bind_front #if !defined(__cpp_lib_bind_back) -# if (__cplusplus >= 202100L) && (__cpp_explicit_this_parameter) +# if (__cplusplus > 202302L) +# define __glibcxx_bind_back 202306L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_bind_back) +# define __cpp_lib_bind_back 202306L +# endif +# elif (__cplusplus >= 202100L) && (__cpp_explicit_this_parameter) # define __glibcxx_bind_back 202202L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_bind_back) # define __cpp_lib_bind_back 202202L diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index 307bcb95bcc..21f0b1cb2d5 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -940,7 +940,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_bound_args(std::forward<_Args>(__args)...) { static_assert(sizeof...(_Args) == sizeof...(_BoundArgs)); } -#if __cpp_explicit_this_parameter +#ifdef __cpp_explicit_this_parameter template<typename _Self, typename... _CallArgs> constexpr invoke_result_t<__like_t<_Self, _Fd>, __like_t<_Self, _BoundArgs>..., _CallArgs...> @@ -1218,8 +1218,130 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return _Not_fn<std::decay_t<_Fn>>{std::forward<_Fn>(__fn), 0}; } +#if __cpp_lib_not_fn >= 202306 + /** Wrap a function type to create a function object that negates its result. + * + * The function template `std::not_fn` creates a "forwarding call wrapper", + * which is a function object that when called forwards its arguments to + * its invocable template argument. + * + * The result of invoking the wrapper is the negation (using `!`) of + * the wrapped function object. + * + * @ingroup functors + * @since C++26 + */ + template<auto __fn> + constexpr auto + not_fn() noexcept + { + using _Fn = decltype(__fn); + if constexpr (is_pointer_v<_Fn> or is_member_pointer_v<_Fn>) { + static_assert(__fn != nullptr); + } + return []<typename... _Tp> (_Tp&&... __call_args) + noexcept(is_nothrow_invocable_v<_Fn, _Tp...>) + -> invoke_result_t<_Fn, _Tp...> + { + return !invoke(__fn, forward<_Tp>(__call_args)...); + }; + } +#endif // __cpp_lib_not_fn >= 202306 +#endif // __cpp_lib_not_fn + +#if __cpp_lib_bind_front >= 202306 || __cpp_lib_bind_back >= 202306 + + template<typename _Tp, typename _Up> + using __copy_const = conditional<is_const_v<_Tp>, const _Up, _Up>; + + template<typename _Tp, typename _Up, + typename _Xp = __copy_const<remove_reference_t<_Tp>, _Up>::type> + using __copy_value_category = + conditional<is_lvalue_reference_v<_Tp&&>, _Xp&, _Xp&&>; + + template<typename _Tp, typename _Up> + using __type_forward_like = + __copy_value_category<_Tp, remove_reference_t<_Up>>; + + template<typename _Tp, typename _Up> + using __type_forward_like_t = __type_forward_like<_Tp, _Up>::type; + #endif +#if __cpp_lib_bind_front >= 202306 + /** Create call wrapper by partial application of arguments to function. + * + * The result of `std::bind_front<f>(args...)` is a function object that + * stores the bound arguments, `args...`. When that function object is + * invoked with `call_args...` it returns the result of calling + * `f(args..., call_args...)`. + * + * @since C++26 + */ + + template<auto __fn, typename... _Args> + constexpr auto + bind_front(_Args&&... __args) + requires ( + (is_constructible_v<decay_t<_Args>, _Args> and ...) and + (is_move_constructible_v<decay_t<_Args>> and ...)) + { + using _Fn = decltype(__fn); + if constexpr (is_pointer_v<_Fn> or is_member_pointer_v<_Fn>) { + static_assert(__fn != nullptr); + } + return [... __bound_args(std::forward<_Args>(__args))]< + typename _Self, typename... _Tp> + (this _Self&&, _Tp&&... __call_args) + noexcept(is_nothrow_invocable_v< + _Fn, __type_forward_like_t<_Self, decay_t<_Args>>..., _Tp...>) + -> invoke_result_t< + _Fn, __type_forward_like_t<_Self, decay_t<_Args>>..., _Tp...> + { + return invoke(__fn, + forward_like<_Self>(__bound_args)..., + forward<_Tp>(__call_args)...); + }; + } +#endif // __cpp_lib_bind_front + +#if __cpp_lib_bind_back >= 202306 + /** Create call wrapper by partial application of arguments to function. + * + * The result of `std::bind_back<f>(args...)` is a function object that + * stores the bound arguments, `args...`. When that function object is + * invoked with `call_args...` it returns the result of calling + * `f(call_args..., args...)`. + * + * @since C++26 + */ + + template<auto __fn, typename... _Args> + constexpr auto + bind_back(_Args&&... __args) + requires ( + (is_constructible_v<decay_t<_Args>, _Args> and ...) and + (is_move_constructible_v<decay_t<_Args>> and ...)) + { + using _Fn = decltype(__fn); + if constexpr (is_pointer_v<_Fn> or is_member_pointer_v<_Fn>) { + static_assert(__fn != nullptr); + } + return [... __bound_args(std::forward<_Args>(__args))]< + typename _Self, typename... _Tp> + (this _Self&&, _Tp&&... __call_args) + noexcept(is_nothrow_invocable_v< + _Fn, _Tp..., __type_forward_like_t<_Self, decay_t<_Args>>...>) + -> invoke_result_t< + _Fn, _Tp..., __type_forward_like_t<_Self, decay_t<_Args>>...> + { + return invoke(__fn, + forward<_Tp>(__call_args)..., + forward_like<_Self>(__bound_args)...); + }; + } +#endif // __cpp_lib_bind_back + #if __cplusplus >= 201703L // Searchers diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc index 57482c52263..d21432f9629 100644 --- a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc @@ -47,6 +47,18 @@ test01() decltype(bind_front(std::declval<F>(), std::declval<int>())), decltype(bind_front(std::declval<const F&>(), std::declval<const int&>())) >); +#if __cpp_lib_bind_front >= 202306 + const F f{}; + + static_assert(std::is_same_v< + decltype(bind_front<f>(std::declval<int>())), + decltype(bind_front<f>(std::declval<int&>())) + >); + static_assert(std::is_same_v< + decltype(bind_front<f>(std::declval<int>())), + decltype(bind_front<f>(std::declval<const int&>())) + >); +#endif // Reference wrappers should be handled: static_assert(!std::is_same_v< @@ -61,6 +73,20 @@ test01() decltype(bind_front(std::declval<F>(), std::ref(std::declval<int&>()))), decltype(bind_front(std::declval<F>(), std::cref(std::declval<int&>()))) >); +#if __cpp_lib_bind_front >= 202306 + static_assert(!std::is_same_v< + decltype(bind_front<f>(std::declval<int&>())), + decltype(bind_front<f>(std::ref(std::declval<int&>()))) + >); + static_assert(!std::is_same_v< + decltype(bind_front<f>(std::declval<const int&>())), + decltype(bind_front<f>(std::cref(std::declval<int&>()))) + >); + static_assert(!std::is_same_v< + decltype(bind_front<f>(std::ref(std::declval<int&>()))), + decltype(bind_front<f>(std::cref(std::declval<int&>()))) + >); +#endif } void @@ -81,6 +107,7 @@ test02() }; F f; + auto g = bind_front(f); const auto& cg = g; quals q; @@ -94,6 +121,23 @@ test02() VERIFY( q.as_const && q.as_lvalue ); q = std::move(cg)(); VERIFY( q.as_const && ! q.as_lvalue ); + +#if __cpp_lib_bind_front >= 202306 + const F f2; + auto g2 = bind_front<f2>(); + const auto& cg2 = g2; + quals q2; + + // constness and value category should be forwarded to the target object: + q2 = g2(); + VERIFY( ! q2.as_const && q2.as_lvalue ); + q2 = std::move(g)(); + VERIFY( ! q2.as_const && ! q2.as_lvalue ); + q2 = cg2(); + VERIFY( q2.as_const && q2.as_lvalue ); + q2 = std::move(cg2)(); + VERIFY( q2.as_const && ! q2.as_lvalue ); +#endif } void @@ -147,9 +191,53 @@ test03() static_assert(is_invocable_r_v<int&, G4&&>); static_assert(is_invocable_r_v<void*, const G4&>); static_assert(is_invocable_r_v<void*, const G4&&>); + +#if __cpp_lib_bind_front >= 202306 + constexpr static F f{}; + + auto g5 = bind_front<f>(i); // call wrapper has bound arg of type int + using G5 = decltype(g5); + // Invoking G5& will pass g5's bound arg as int&, so calls first overload: + static_assert(is_invocable_r_v<int&, G5&, void*>); + // Invoking const G5& or G&& calls second overload: + static_assert(is_invocable_r_v<void*, const G5&, void*>); + static_assert(is_invocable_r_v<void*, G5&&, void*>); + void* p5 = static_cast<G5&&>(g5)(vp); + VERIFY( p5 == vp ); + + auto g6 = bind_front<f>(std::ref(i)); // bound arg of type int& + using G6 = decltype(g6); + // Bound arg always forwarded as int& even from G6&& or const G6& + static_assert(is_invocable_r_v<int&, G6&, void*>); + static_assert(is_invocable_r_v<int&, G6&&, void*>); + // But cannot call first overload on const G6: + static_assert(is_invocable_r_v<void*, const G6&, void*>); + static_assert(is_invocable_r_v<void*, const G6&&, void*>); + int& i6 = g6(vp); + VERIFY( &i6 == &i ); + int& i6r = static_cast<G6&&>(g6)(vp); + VERIFY( &i6r == &i ); + void* p6 = const_cast<const G6&>(g6)(vp); + VERIFY( p6 == vp ); + + auto g7 = bind_front<f>(std::cref(i)); // bound arg of type const int& + using G7 = decltype(g7); + // Bound arg always forwarded as const int& so can only call second overload: + static_assert(is_invocable_r_v<void*, G7&, void*>); + static_assert(is_invocable_r_v<void*, G7&&, void*>); + static_assert(is_invocable_r_v<void*, const G7&, void*>); + static_assert(is_invocable_r_v<void*, const G7&&, void*>); + + auto g8 = bind_front<g7>(nullptr); + using G8 = decltype(g8); + static_assert(is_invocable_r_v<int&, G8&>); + static_assert(is_invocable_r_v<int&, G8&&>); + static_assert(is_invocable_r_v<void*, const G8&>); + static_assert(is_invocable_r_v<void*, const G8&&>); +#endif } -int f(int i, int j, int k) { return i + j + k; } +constexpr int f(int i, int j, int k) { return i + j + k; } void test04() @@ -165,6 +253,19 @@ test04() auto g3 = bind_front(f, 1, 2, 3); VERIFY( g3() == 6 ); VERIFY( bind_front(g2, 3)() == 6 ); +#if __cpp_lib_bind_front >= 202306 + const auto g4 = bind_front<f>(); + VERIFY( g4(1, 2, 3) == 6 ); + const auto g5 = bind_front<f>(1); + VERIFY( g5(2, 3) == 6 ); + VERIFY( bind_front<g4>(1)(2, 3) == 6 ); + const auto g6 = bind_front<f>(1, 2); + VERIFY( g6(3) == 6 ); + VERIFY( bind_front<g5>(2)(3) == 6 ); + const auto g7 = bind_front<f>(1, 2, 3); + VERIFY( g7() == 6 ); + VERIFY( bind_front<g6>(3)() == 6 ); +#endif } int -- 2.50.0