From 9ae2a7c39995fe2830360d87331500efc1fc6f15 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Thu, 7 Mar 2019 14:15:53 +0000 Subject: [PATCH] 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. From-SVN: r269455 --- libstdc++-v3/ChangeLog | 8 + libstdc++-v3/include/std/functional | 100 ++++++++++++ .../20_util/function_objects/bind_front/1.cc | 176 +++++++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100644 libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 2559fa3..4aa4c5e 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,11 @@ +2019-03-07 Jonathan Wakely + + 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. + 2019-03-06 Jonathan Wakely * include/std/type_traits (__cpp_lib_bounded_array_traits): Define. diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index 911a041..8cf2c67 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 + 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 + explicit constexpr + _Bind_front(int, _Fn&& __fn, _Args&&... __args) + noexcept(__and_, + 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 + 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 + constexpr + invoke_result_t + operator()(_CallArgs&&... __call_args) const & + noexcept(is_nothrow_invocable_v) + { + return _S_call(*this, _BoundIndices(), + std::forward<_CallArgs>(__call_args)...); + } + + template + 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 + constexpr + invoke_result_t + operator()(_CallArgs&&... __call_args) const && + noexcept(is_nothrow_invocable_v) + { + return _S_call(std::move(*this), _BoundIndices(), + std::forward<_CallArgs>(__call_args)...); + } + + private: + using _BoundIndices = index_sequence_for<_BoundArgs...>; + + template + 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 + using _Bind_front_t + = _Bind_front, unwrap_ref_decay_t<_Args>...>; + + template + _Bind_front_t<_Fn, _Args...> + bind_front(_Fn&& __fn, _Args&&... __args) + noexcept(is_nothrow_constructible_v, + _Fn, _Args...>) + { + return _Bind_front_t<_Fn, _Args...>(0, std::forward<_Fn>(__fn), + std::forward<_Args>(__args)...); + } +#endif + #if __cplusplus >= 201402L /// Generalized negator. template 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 0000000..eea31e9 --- /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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include +#include + +#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(), std::declval())), + decltype(bind_front(std::declval(), std::declval())) + >); + static_assert(std::is_same_v< + decltype(bind_front(std::declval(), std::declval())), + decltype(bind_front(std::declval(), std::declval())) + >); + + // Reference wrappers should be handled: + static_assert(!std::is_same_v< + decltype(bind_front(std::declval(), std::declval())), + decltype(bind_front(std::declval(), std::ref(std::declval()))) + >); + static_assert(!std::is_same_v< + decltype(bind_front(std::declval(), std::declval())), + decltype(bind_front(std::declval(), std::cref(std::declval()))) + >); + static_assert(!std::is_same_v< + decltype(bind_front(std::declval(), std::ref(std::declval()))), + decltype(bind_front(std::declval(), std::cref(std::declval()))) + >); +} + +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); + // Invoking const G1& or G&& calls second overload: + static_assert(is_invocable_r_v); + static_assert(is_invocable_r_v); + void* p1 = static_cast(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); + static_assert(is_invocable_r_v); + // But cannot call first overload on const G2: + static_assert(is_invocable_r_v); + static_assert(is_invocable_r_v); + int& i2 = g2(vp); + VERIFY( &i2 == &i ); + int& i2r = static_cast(g2)(vp); + VERIFY( &i2r == &i ); + void* p2 = const_cast(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); + static_assert(is_invocable_r_v); + static_assert(is_invocable_r_v); + static_assert(is_invocable_r_v); + + auto g4 = bind_front(g2, nullptr); + using G4 = decltype(g4); + static_assert(is_invocable_r_v); + static_assert(is_invocable_r_v); + static_assert(is_invocable_r_v); + static_assert(is_invocable_r_v); +} + +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(); +} -- 2.7.4