* include/std/functional [C++20] (_Bind_front, _Bind_front_t): Define helpers for bind_front. (bind_front, __cpp_lib_bind_front): Define. * testsuite/20_util/function_objects/bind_front/1.cc: New test.
Tested powerpc64le-linux, committed to trunk.
commit 1944c1ed745ed945860d9e28ff48e3d7436e6ba3 Author: Jonathan Wakely <jwak...@redhat.com> Date: Thu Mar 7 13:06:52 2019 +0000 P0356R5 Simplified partial function application * include/std/functional [C++20] (_Bind_front, _Bind_front_t): Define helpers for bind_front. (bind_front, __cpp_lib_bind_front): Define. * testsuite/20_util/function_objects/bind_front/1.cc: New test. diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index 911a041cba5..8cf2c670648 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -836,6 +836,106 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::forward<_BoundArgs>(__args)...); } +#if __cplusplus > 201703L +#define __cpp_lib_bind_front 201902L + + template<typename _Fd, typename... _BoundArgs> + struct _Bind_front + { + static_assert(is_move_constructible_v<_Fd>); + static_assert((is_move_constructible_v<_BoundArgs> && ...)); + + // First parameter is to ensure this constructor is never used + // instead of the copy/move constructor. + template<typename _Fn, typename... _Args> + explicit constexpr + _Bind_front(int, _Fn&& __fn, _Args&&... __args) + noexcept(__and_<is_nothrow_constructible<_Fd, _Fn>, + is_nothrow_constructible<_BoundArgs, _Args>...>::value) + : _M_fd(std::forward<_Fn>(__fn)), + _M_bound_args(std::forward<_Args>(__args)...) + { static_assert(sizeof...(_Args) == sizeof...(_BoundArgs)); } + + _Bind_front(const _Bind_front&) = default; + _Bind_front(_Bind_front&&) = default; + _Bind_front& operator=(const _Bind_front&) = default; + _Bind_front& operator=(_Bind_front&&) = default; + ~_Bind_front() = default; + + template<typename... _CallArgs> + constexpr + invoke_result_t<_Fd&, _BoundArgs&..., _CallArgs...> + operator()(_CallArgs&&... __call_args) & + noexcept(is_nothrow_invocable_v<_Fd&, _BoundArgs&..., _CallArgs...>) + { + return _S_call(*this, _BoundIndices(), + std::forward<_CallArgs>(__call_args)...); + } + + template<typename... _CallArgs> + constexpr + invoke_result_t<const _Fd&, const _BoundArgs&..., _CallArgs...> + operator()(_CallArgs&&... __call_args) const & + noexcept(is_nothrow_invocable_v<const _Fd&, const _BoundArgs&..., + _CallArgs...>) + { + return _S_call(*this, _BoundIndices(), + std::forward<_CallArgs>(__call_args)...); + } + + template<typename... _CallArgs> + constexpr + invoke_result_t<_Fd, _BoundArgs..., _CallArgs...> + operator()(_CallArgs&&... __call_args) && + noexcept(is_nothrow_invocable_v<_Fd, _BoundArgs..., _CallArgs...>) + { + return _S_call(std::move(*this), _BoundIndices(), + std::forward<_CallArgs>(__call_args)...); + } + + template<typename... _CallArgs> + constexpr + invoke_result_t<const _Fd, const _BoundArgs..., _CallArgs...> + operator()(_CallArgs&&... __call_args) const && + noexcept(is_nothrow_invocable_v<const _Fd, const _BoundArgs..., + _CallArgs...>) + { + return _S_call(std::move(*this), _BoundIndices(), + std::forward<_CallArgs>(__call_args)...); + } + + private: + using _BoundIndices = index_sequence_for<_BoundArgs...>; + + template<typename _Tp, size_t... _Ind, typename... _CallArgs> + static constexpr + decltype(auto) + _S_call(_Tp&& __g, index_sequence<_Ind...>, _CallArgs&&... __call_args) + { + return std::invoke(std::forward<_Tp>(__g)._M_fd, + std::get<_Ind>(std::forward<_Tp>(__g)._M_bound_args)..., + std::forward<_CallArgs>(__call_args)...); + } + + _Fd _M_fd; + std::tuple<_BoundArgs...> _M_bound_args; + }; + + template<typename _Fn, typename... _Args> + using _Bind_front_t + = _Bind_front<decay_t<_Fn>, unwrap_ref_decay_t<_Args>...>; + + template<typename _Fn, typename... _Args> + _Bind_front_t<_Fn, _Args...> + bind_front(_Fn&& __fn, _Args&&... __args) + noexcept(is_nothrow_constructible_v<int, _Bind_front_t<_Fn, _Args...>, + _Fn, _Args...>) + { + return _Bind_front_t<_Fn, _Args...>(0, std::forward<_Fn>(__fn), + std::forward<_Args>(__args)...); + } +#endif + #if __cplusplus >= 201402L /// Generalized negator. template<typename _Fn> 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 new file mode 100644 index 00000000000..eea31e9e8a5 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc @@ -0,0 +1,176 @@ +// Copyright (C) 2014-2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include <functional> +#include <testsuite_hooks.h> + +#ifndef __cpp_lib_bind_front +# error "Feature test macro for bind_front is missing" +#elif __cpp_lib_bind_front < 201811L +# error "Feature test macro for bind_front has wrong value" +#endif + +using std::bind_front; +using std::is_same_v; +using std::is_invocable_v; +using std::is_invocable_r_v; + +void +test01() +{ + struct F { void operator()() {} }; + + // Arguments should be decayed: + static_assert(std::is_same_v< + decltype(bind_front(std::declval<F>(), std::declval<int>())), + decltype(bind_front(std::declval<F&>(), std::declval<int&>())) + >); + static_assert(std::is_same_v< + decltype(bind_front(std::declval<F>(), std::declval<int>())), + decltype(bind_front(std::declval<const F&>(), std::declval<const int&>())) + >); + + // Reference wrappers should be handled: + static_assert(!std::is_same_v< + decltype(bind_front(std::declval<F>(), std::declval<int&>())), + decltype(bind_front(std::declval<F>(), std::ref(std::declval<int&>()))) + >); + static_assert(!std::is_same_v< + decltype(bind_front(std::declval<F>(), std::declval<const int&>())), + decltype(bind_front(std::declval<F>(), std::cref(std::declval<int&>()))) + >); + static_assert(!std::is_same_v< + decltype(bind_front(std::declval<F>(), std::ref(std::declval<int&>()))), + decltype(bind_front(std::declval<F>(), std::cref(std::declval<int&>()))) + >); +} + +void +test02() +{ + struct quals + { + bool as_const; + bool as_lvalue; + }; + + struct F + { + quals operator()() & { return { false, true }; } + quals operator()() const & { return { true, true }; } + quals operator()() && { return { false, false }; } + quals operator()() const && { return { true, false }; } + }; + + F f; + auto g = bind_front(f); + const auto& cg = g; + quals q; + + // constness and value category should be forwarded to the target object: + q = g(); + VERIFY( ! q.as_const && q.as_lvalue ); + std::move(g)(); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cg)(); + VERIFY( q.as_const && ! q.as_lvalue ); +} + +void +test03() +{ + struct F + { + int& operator()(int& i, void*) { return i; } + void* operator()(int, void* p) const { return p; } + }; + + int i = 5; + void* vp = &vp; // arbitrary void* value + + auto g1 = bind_front(F{}, i); // call wrapper has bound arg of type int + using G1 = decltype(g1); + // Invoking G1& will pass g1's bound arg as int&, so calls first overload: + static_assert(is_invocable_r_v<int&, G1&, void*>); + // Invoking const G1& or G&& calls second overload: + static_assert(is_invocable_r_v<void*, const G1&, void*>); + static_assert(is_invocable_r_v<void*, G1&&, void*>); + void* p1 = static_cast<G1&&>(g1)(vp); + VERIFY( p1 == vp ); + + auto g2 = bind_front(F{}, std::ref(i)); // bound arg of type int& + using G2 = decltype(g2); + // Bound arg always forwarded as int& even from G2&& or const G2& + static_assert(is_invocable_r_v<int&, G2&, void*>); + static_assert(is_invocable_r_v<int&, G2&&, void*>); + // But cannot call first overload on const G2: + static_assert(is_invocable_r_v<void*, const G2&, void*>); + static_assert(is_invocable_r_v<void*, const G2&&, void*>); + int& i2 = g2(vp); + VERIFY( &i2 == &i ); + int& i2r = static_cast<G2&&>(g2)(vp); + VERIFY( &i2r == &i ); + void* p2 = const_cast<const G2&>(g2)(vp); + VERIFY( p2 == vp ); + + auto g3 = bind_front(F{}, std::cref(i)); // bound arg of type const int& + using G3 = decltype(g3); + // Bound arg always forwarded as const int& so can only call second overload: + static_assert(is_invocable_r_v<void*, G3&, void*>); + static_assert(is_invocable_r_v<void*, G3&&, void*>); + static_assert(is_invocable_r_v<void*, const G3&, void*>); + static_assert(is_invocable_r_v<void*, const G3&&, void*>); + + auto g4 = bind_front(g2, nullptr); + using G4 = decltype(g4); + static_assert(is_invocable_r_v<int&, G4&>); + 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&&>); +} + +int f(int i, int j, int k) { return i + j + k; } + +void +test04() +{ + auto g = bind_front(f); + VERIFY( g(1, 2, 3) == 6 ); + auto g1 = bind_front(f, 1); + VERIFY( g1(2, 3) == 6 ); + VERIFY( bind_front(g, 1)(2, 3) == 6 ); + auto g2 = bind_front(f, 1, 2); + VERIFY( g2(3) == 6 ); + VERIFY( bind_front(g1, 2)(3) == 6 ); + auto g3 = bind_front(f, 1, 2, 3); + VERIFY( g3() == 6 ); + VERIFY( bind_front(g2, 3)() == 6 ); +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +}