Re: [PATCH] P0356R5 Simplified partial function application

2019-03-07 Thread Jonathan Wakely

On 07/03/19 14:15 +, Jonathan Wakely wrote:

* 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.


I'm considering something like the attached patch (but with better
names for __tag1 and __tag2 obviously). With this change bind_front
would unwrap nested binders, so that bind_front(bind_front(f, 1), 2)
would create a _Bind_front instead of
_Bind_front<_Bind_front, int>.

That would make the call go straight to the target object, instead of
through an extra layer of wrapper (which should improve compile times,
and also unoptimized runtime performance).



diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 8cf2c670648..da61b00bd15 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -839,6 +839,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #if __cplusplus > 201703L
 #define __cpp_lib_bind_front 201902L
 
+  struct __tag1;
+  struct __tag2;
+
   template
 struct _Bind_front
 {
@@ -849,13 +852,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // instead of the copy/move constructor.
   template
 	explicit constexpr
-	_Bind_front(int, _Fn&& __fn, _Args&&... __args)
+	_Bind_front(__tag1*, _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)); }
 
+  template
+	explicit constexpr
+	_Bind_front(__tag2*, const _Bind_front<_Fd, _BArgs...>& __fn,
+		_Args&&... __args)
+	: _M_fd(__fn._M_fd),
+	  _M_bound_args(std::tuple_cat(__fn._M_bound_args,
+		std::make_tuple(std::forward<_Args>(__args)...)))
+	{ }
+
+  template
+	explicit constexpr
+	_Bind_front(__tag2*, _Bind_front<_Fd, _BArgs...>&& __fn,
+		_Args&&... __args)
+	: _M_fd(std::move(__fn._M_fd)),
+	  _M_bound_args(std::tuple_cat(std::move(__fn._M_bound_args),
+		std::make_tuple(std::forward<_Args>(__args)...)))
+	{ }
+
   _Bind_front(const _Bind_front&) = default;
   _Bind_front(_Bind_front&&) = default;
   _Bind_front& operator=(const _Bind_front&) = default;
@@ -919,19 +940,42 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   _Fd _M_fd;
   std::tuple<_BoundArgs...> _M_bound_args;
+
+  template
+	friend class _Bind_front;
 };
 
   template
-using _Bind_front_t
-  = _Bind_front, unwrap_ref_decay_t<_Args>...>;
+struct _Bind_front_helper
+{
+  using type = _Bind_front<_Fn, _Args...>;
+  using __tag = __tag1*;
+};
+
+  template
+struct _Bind_front_helper<_Bind_front<_Fn, _BoundArgs...>, _Args...>
+{
+  using type = _Bind_front<_Fn, _BoundArgs..., _Args...>;
+  using __tag = __tag2*;
+};
+
+  template
+using _Bind_front_t = typename
+  _Bind_front_helper, unwrap_ref_decay_t<_Args>...>::type;
+
+  template
+using _Bind_front_tag = typename
+  _Bind_front_helper, unwrap_ref_decay_t<_Args>...>::__tag;
 
   template
 _Bind_front_t<_Fn, _Args...>
 bind_front(_Fn&& __fn, _Args&&... __args)
-noexcept(is_nothrow_constructible_v,
+noexcept(is_nothrow_constructible_v<_Bind_front_tag<_Fn, _Args...>,
+	_Bind_front_t<_Fn, _Args...>,
 	_Fn, _Args...>)
 {
-  return _Bind_front_t<_Fn, _Args...>(0, std::forward<_Fn>(__fn),
+  return _Bind_front_t<_Fn, _Args...>(_Bind_front_tag<_Fn, _Args...>(),
+	  std::forward<_Fn>(__fn),
 	  std::forward<_Args>(__args)...);
 }
 #endif


Re: [PATCH] P0356R5 Simplified partial function application

2019-03-07 Thread Jonathan Wakely

On 07/03/19 14:15 +, Jonathan Wakely wrote:

* 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.


The new test had a typo, which wasn't noticed because the test was
only being compiled, not executed. Fixed like so, committed to trunk.

commit 2d3fddd4358a7ab0f92aa3295c7ac04c8dc6390f
Author: Jonathan Wakely 
Date:   Thu Mar 7 14:34:21 2019 +

Fix new test to run as well as compile

* testsuite/20_util/function_objects/bind_front/1.cc: Change from
compile test to run. Fix typo.

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 eea31e9e8a5..8ebc2bab41a 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
@@ -16,7 +16,7 @@
 // .
 
 // { dg-options "-std=gnu++2a" }
-// { dg-do compile { target c++2a } }
+// { dg-do run { target c++2a } }
 
 #include 
 #include 
@@ -87,7 +87,7 @@ test02()
   // constness and value category should be forwarded to the target object:
   q = g();
   VERIFY( ! q.as_const && q.as_lvalue );
-  std::move(g)();
+  q = std::move(g)();
   VERIFY( ! q.as_const && ! q.as_lvalue );
   q = cg();
   VERIFY( q.as_const && q.as_lvalue );


[PATCH] P0356R5 Simplified partial function application

2019-03-07 Thread Jonathan Wakely

* 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 
Date:   Thu Mar 7 13:06:52 2019 +

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
+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 000..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