Commit 9ae2a7c3 by Jonathan Wakely Committed by 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.

From-SVN: r269455
parent 20faba6d
2019-03-07 Jonathan Wakely <jwakely@redhat.com>
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 <jwakely@redhat.com> 2019-03-06 Jonathan Wakely <jwakely@redhat.com>
* include/std/type_traits (__cpp_lib_bounded_array_traits): Define. * include/std/type_traits (__cpp_lib_bounded_array_traits): Define.
......
...@@ -836,6 +836,106 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -836,6 +836,106 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::forward<_BoundArgs>(__args)...); 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 #if __cplusplus >= 201402L
/// Generalized negator. /// Generalized negator.
template<typename _Fn> template<typename _Fn>
......
// 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();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment