public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] P0356R5 Simplified partial function application
@ 2019-03-07 14:18 Jonathan Wakely
  2019-03-07 14:38 ` Jonathan Wakely
  2019-03-07 14:41 ` Jonathan Wakely
  0 siblings, 2 replies; 3+ messages in thread
From: Jonathan Wakely @ 2019-03-07 14:18 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 255 bytes --]

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



[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 10128 bytes --]

commit 1944c1ed745ed945860d9e28ff48e3d7436e6ba3
Author: Jonathan Wakely <jwakely@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();
+}

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] P0356R5 Simplified partial function application
  2019-03-07 14:18 [PATCH] P0356R5 Simplified partial function application Jonathan Wakely
@ 2019-03-07 14:38 ` Jonathan Wakely
  2019-03-07 14:41 ` Jonathan Wakely
  1 sibling, 0 replies; 3+ messages in thread
From: Jonathan Wakely @ 2019-03-07 14:38 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 397 bytes --]

On 07/03/19 14:15 +0000, 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.


[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 1139 bytes --]

commit 2d3fddd4358a7ab0f92aa3295c7ac04c8dc6390f
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Mar 7 14:34:21 2019 +0000

    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 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-options "-std=gnu++2a" }
-// { dg-do compile { target c++2a } }
+// { dg-do run { target c++2a } }
 
 #include <functional>
 #include <testsuite_hooks.h>
@@ -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 );

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] P0356R5 Simplified partial function application
  2019-03-07 14:18 [PATCH] P0356R5 Simplified partial function application Jonathan Wakely
  2019-03-07 14:38 ` Jonathan Wakely
@ 2019-03-07 14:41 ` Jonathan Wakely
  1 sibling, 0 replies; 3+ messages in thread
From: Jonathan Wakely @ 2019-03-07 14:41 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 741 bytes --]

On 07/03/19 14:15 +0000, 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<F, int, int> instead of
_Bind_front<_Bind_front<F, int>, 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).




[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 3399 bytes --]

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<typename _Fd, typename... _BoundArgs>
     struct _Bind_front
     {
@@ -849,13 +852,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // instead of the copy/move constructor.
       template<typename _Fn, typename... _Args>
 	explicit constexpr
-	_Bind_front(int, _Fn&& __fn, _Args&&... __args)
+	_Bind_front(__tag1*, _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)); }
 
+      template<typename... _BArgs, typename... _Args>
+	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<typename... _BArgs, typename... _Args>
+	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<typename _Fd2, typename... _BArgs>
+	friend class _Bind_front;
     };
 
   template<typename _Fn, typename... _Args>
-    using _Bind_front_t
-      = _Bind_front<decay_t<_Fn>, unwrap_ref_decay_t<_Args>...>;
+    struct _Bind_front_helper
+    {
+      using type = _Bind_front<_Fn, _Args...>;
+      using __tag = __tag1*;
+    };
+
+  template<typename _Fn, typename... _BoundArgs, typename... _Args>
+    struct _Bind_front_helper<_Bind_front<_Fn, _BoundArgs...>, _Args...>
+    {
+      using type = _Bind_front<_Fn, _BoundArgs..., _Args...>;
+      using __tag = __tag2*;
+    };
+
+  template<typename _Fn, typename... _Args>
+    using _Bind_front_t = typename
+      _Bind_front_helper<decay_t<_Fn>, unwrap_ref_decay_t<_Args>...>::type;
+
+  template<typename _Fn, typename... _Args>
+    using _Bind_front_tag = typename
+      _Bind_front_helper<decay_t<_Fn>, unwrap_ref_decay_t<_Args>...>::__tag;
 
   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...>,
+    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

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2019-03-07 14:38 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-07 14:18 [PATCH] P0356R5 Simplified partial function application Jonathan Wakely
2019-03-07 14:38 ` Jonathan Wakely
2019-03-07 14:41 ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).