diff mbox

add varargs support to std::mem_fn

Message ID 20141111233844.GO5191@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely Nov. 11, 2014, 11:38 p.m. UTC
My recent patch to add ref-qualifier support to std::mem_fn didn't
cover C-style varargs functions (as opposed to C++ variadic templates,
which work fine). Rather than double the number of partial
specializations this generates them using a macro.

The arity-checking needs to be adjusted to allow for additional
arguments being passed through the ellipsis.

This also adds a __bool_constant alias which I've been toying with
adding for some time. LEWG are in favour of adding std::bool_constant
to C++17 so adding __bool_constant now means we can use it even in
C++11 mode.

std::reference_wrapper still doesn't define the required typedefs when
it wraps a pointer to ref-qualified member function, but that will
have to wait for now. At some point we'll also need to implement the
std::invoke() function that has been voted into C++17 and we might
want to refactor all this code again.

Tested x86_64-linux, committed to trunk.
diff mbox

Patch

commit 07591983a7880e2370fbc10020e0d9cdfaa86678
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Nov 11 13:44:45 2014 +0000

    Make std::mem_fn work with varargs functions.
    
    	* include/std/functional (_Mem_fn_traits): Add partial specializations
    	for varargs functions.
    	(_Mem_fn_base): Do not check arguments are convertible for varargs.
    	(_Bind_check_arity): Add partial specializations for varargs functions.
    	* include/std/type_traits (__bool_constant): Add alias template.
    	* testsuite/20_util/bind/ref_neg.cc: Adjust dg-error.
    	* testsuite/20_util/bind/refqual.cc: New, test ref-qualifiers.
    	* testsuite/20_util/declval/requirements/1_neg.cc: Adjust dg-error.
    	* testsuite/20_util/function_objects/mem_fn/refqual.cc: Test varargs.
    	* testsuite/20_util/make_signed/requirements/typedefs_neg.cc: Adjust
    	dg-error.
    	* testsuite/20_util/make_unsigned/requirements/typedefs_neg.cc: Adjust
    	dg-error.

diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 25ec1b3..92489b7 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -512,113 +512,38 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
       using __arity = integral_constant<size_t, sizeof...(_ArgTypes)>;
     };
 
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...)>
-    : _Mem_fn_traits_base<_Res, _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...);
-      using __lvalue = true_type;
-      using __rvalue = true_type;
-    };
-
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...) const>
-    : _Mem_fn_traits_base<_Res, const _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...) const;
-      using __lvalue = true_type;
-      using __rvalue = true_type;
+#define _GLIBCXX_MEM_FN_TRAITS2(_CV, _REF, _LVAL, _RVAL)		\
+  template<typename _Res, typename _Class, typename... _ArgTypes>	\
+    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...) _CV _REF>	\
+    : _Mem_fn_traits_base<_Res, _CV _Class, _ArgTypes...>		\
+    {									\
+      using __pmf_type  = _Res (_Class::*)(_ArgTypes...) _CV _REF;	\
+      using __lvalue = _LVAL;						\
+      using __rvalue = _RVAL;						\
+      using __vararg = false_type;					\
+    };									\
+  template<typename _Res, typename _Class, typename... _ArgTypes>	\
+    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes... ...) _CV _REF>	\
+    : _Mem_fn_traits_base<_Res, _CV _Class, _ArgTypes...>		\
+    {									\
+      using __pmf_type  = _Res (_Class::*)(_ArgTypes... ...) _CV _REF;	\
+      using __lvalue = _LVAL;						\
+      using __rvalue = _RVAL;						\
+      using __vararg = true_type;					\
     };
 
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...) volatile>
-    : _Mem_fn_traits_base<_Res, volatile _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...) volatile;
-      using __lvalue = true_type;
-      using __rvalue = true_type;
-    };
+#define _GLIBCXX_MEM_FN_TRAITS(_REF, _LVAL, _RVAL)		\
+  _GLIBCXX_MEM_FN_TRAITS2(		, _REF, _LVAL, _RVAL)	\
+  _GLIBCXX_MEM_FN_TRAITS2(const		, _REF, _LVAL, _RVAL)	\
+  _GLIBCXX_MEM_FN_TRAITS2(volatile	, _REF, _LVAL, _RVAL)	\
+  _GLIBCXX_MEM_FN_TRAITS2(const volatile, _REF, _LVAL, _RVAL)
 
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...) const volatile>
-    : _Mem_fn_traits_base<_Res, const volatile _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...) const volatile;
-      using __lvalue = true_type;
-      using __rvalue = true_type;
-    };
+_GLIBCXX_MEM_FN_TRAITS( , true_type, true_type)
+_GLIBCXX_MEM_FN_TRAITS(&, true_type, false_type)
+_GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type)
 
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...)&>
-    : _Mem_fn_traits_base<_Res, _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...)&;
-      using __lvalue = true_type;
-      using __rvalue = false_type;
-    };
-
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...) const&>
-    : _Mem_fn_traits_base<_Res, const _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...) const&;
-      using __lvalue = true_type;
-      using __rvalue = false_type;
-    };
-
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...) volatile&>
-    : _Mem_fn_traits_base<_Res, volatile _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...) volatile&;
-      using __lvalue = true_type;
-      using __rvalue = false_type;
-    };
-
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...) const volatile&>
-    : _Mem_fn_traits_base<_Res, const volatile _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...) const volatile&;
-      using __lvalue = true_type;
-      using __rvalue = false_type;
-    };
-
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...)&&>
-    : _Mem_fn_traits_base<_Res, _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...)&&;
-      using __lvalue = false_type;
-      using __rvalue = true_type;
-    };
-
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...) const&&>
-    : _Mem_fn_traits_base<_Res, const _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...) const&&;
-      using __lvalue = false_type;
-      using __rvalue = true_type;
-    };
-
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...) volatile&&>
-    : _Mem_fn_traits_base<_Res, volatile _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...) volatile&&;
-      using __lvalue = false_type;
-      using __rvalue = true_type;
-    };
-
-  template<typename _Res, typename _Class, typename... _ArgTypes>
-    struct _Mem_fn_traits<_Res (_Class::*)(_ArgTypes...) const volatile&&>
-    : _Mem_fn_traits_base<_Res, const volatile _Class, _ArgTypes...>
-    {
-      using __pmf_type  = _Res (_Class::*)(_ArgTypes...) const volatile&&;
-      using __lvalue = false_type;
-      using __rvalue = true_type;
-    };
+#undef _GLIBCXX_MEM_FN_TRAITS
+#undef _GLIBCXX_MEM_FN_TRAITS2
 
   template<typename _MemFunPtr,
 	   bool __is_mem_fn = is_member_function_pointer<_MemFunPtr>::value>
@@ -627,62 +552,40 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
     {
       using _Traits = _Mem_fn_traits<_MemFunPtr>;
 
-    public:
-      using result_type = typename _Traits::__result_type;
-      using _Arity = typename _Traits::__arity;
-
-    private:
       using _Class = typename _Traits::__class_type;
       using _ArgTypes = typename _Traits::__arg_types;
       using _Pmf = typename _Traits::__pmf_type;
 
-      template<typename _Tp, typename... _Args>
-	result_type
-	_M_call(_Tp&& __object, const volatile _Class *,
-		_Args&&... __args) const
-	{
-	  return (std::forward<_Tp>(__object).*_M_pmf)
-	    (std::forward<_Args>(__args)...);
-	}
-
-      template<typename _Tp, typename... _Args>
-	result_type
-	_M_call(_Tp&& __ptr, const volatile void *, _Args&&... __args) const
-	{ return ((*__ptr).*_M_pmf)(std::forward<_Args>(__args)...); }
-
-      // Require each _Args to be convertible to corresponding _ArgTypes
-      template<typename... _Args>
-	using _RequireValidArgs
-	  = _Require<_AllConvertible<_Pack<_Args...>, _ArgTypes>>;
+      using _Arity = typename _Traits::__arity;
+      using _Varargs = typename _Traits::__vararg;
 
-      // Require each _Args to be convertible to corresponding _ArgTypes
-      // and require _Tp is not _Class, _Class& or _Class*
-      template<typename _Tp, typename... _Args>
-	using _RequireValidArgs2
-	  = _Require<_NotSame<_Class, _Tp>, _NotSame<_Class*, _Tp>,
-		     _AllConvertible<_Pack<_Args...>, _ArgTypes>>;
+      template<typename _Func, typename... _BoundArgs>
+	friend struct _Bind_check_arity;
 
-      // Require each _Args to be convertible to corresponding _ArgTypes
-      // and require _Tp is _Class or derived from _Class
-      template<typename _Tp, typename... _Args>
-	using _RequireValidArgs3
-	  = _Require<is_base_of<_Class, _Tp>,
-		     _AllConvertible<_Pack<_Args...>, _ArgTypes>>;
+      // for varargs functions we just check the number of arguments,
+      // otherwise we also check they are convertible.
+      template<typename _Args>
+	using _CheckArgs = typename conditional<_Varargs::value,
+	  __bool_constant<(_Args::value >= _ArgTypes::value)>,
+	  _AllConvertible<_Args, _ArgTypes>
+	>::type;
 
     public:
+      using result_type = typename _Traits::__result_type;
+
       explicit _Mem_fn_base(_Pmf __pmf) : _M_pmf(__pmf) { }
 
       // Handle objects
       template<typename... _Args, typename _Req
                = _Require<typename _Traits::__lvalue,
-                          _AllConvertible<_Pack<_Args...>, _ArgTypes>>>
+                          _CheckArgs<_Pack<_Args...>>>>
 	result_type
 	operator()(_Class& __object, _Args&&... __args) const
 	{ return (__object.*_M_pmf)(std::forward<_Args>(__args)...); }
 
       template<typename... _Args, typename _Req
                = _Require<typename _Traits::__rvalue,
-                          _AllConvertible<_Pack<_Args...>, _ArgTypes>>>
+                          _CheckArgs<_Pack<_Args...>>>>
 	result_type
 	operator()(_Class&& __object, _Args&&... __args) const
 	{
@@ -692,16 +595,15 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
       // Handle pointers
       template<typename... _Args, typename _Req
                = _Require<typename _Traits::__lvalue,
-                          _AllConvertible<_Pack<_Args...>, _ArgTypes>>>
+                          _CheckArgs<_Pack<_Args...>>>>
 	result_type
 	operator()(_Class* __object, _Args&&... __args) const
 	{ return (__object->*_M_pmf)(std::forward<_Args>(__args)...); }
 
       // Handle smart pointers, references and pointers to derived
-      // TODO how to constrain to lvalue/rvalue here? constrain _M_call?
       template<typename _Tp, typename... _Args, typename _Req
                = _Require<_NotSame<_Class, _Tp>, _NotSame<_Class*, _Tp>,
-                          _AllConvertible<_Pack<_Args...>, _ArgTypes>>>
+                          _CheckArgs<_Pack<_Args...>>>>
 	result_type
 	operator()(_Tp&& __object, _Args&&... __args) const
 	{
@@ -711,14 +613,27 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
 
       // Handle reference wrappers
       template<typename _Tp, typename... _Args, typename _Req
-               = _Require<is_base_of<_Class, _Tp>,
-                          typename _Traits::__lvalue,
-                          _AllConvertible<_Pack<_Args...>, _ArgTypes>>>
+               = _Require<is_base_of<_Class, _Tp>, typename _Traits::__lvalue,
+                          _CheckArgs<_Pack<_Args...>>>>
 	result_type
 	operator()(reference_wrapper<_Tp> __ref, _Args&&... __args) const
 	{ return operator()(__ref.get(), std::forward<_Args>(__args)...); }
 
     private:
+      template<typename _Tp, typename... _Args>
+	result_type
+	_M_call(_Tp&& __object, const volatile _Class *,
+		_Args&&... __args) const
+	{
+	  return (std::forward<_Tp>(__object).*_M_pmf)
+	    (std::forward<_Args>(__args)...);
+	}
+
+      template<typename _Tp, typename... _Args>
+	result_type
+	_M_call(_Tp&& __ptr, const volatile void *, _Args&&... __args) const
+	{ return ((*__ptr).*_M_pmf)(std::forward<_Args>(__args)...); }
+
       _Pmf _M_pmf;
     };
 
@@ -750,9 +665,13 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
 	-> decltype((*__ptr).*std::declval<__pm_type&>())
 	{ return (*__ptr).*_M_pm; }
 
-    public:
       using _Arity = integral_constant<size_t, 0>;
+      using _Varargs = false_type;
+
+      template<typename _Func, typename... _BoundArgs>
+	friend struct _Bind_check_arity;
 
+    public:
       explicit
       _Mem_fn_base(_Res _Class::*__pm) noexcept : _M_pm(__pm) { }
 
@@ -1493,12 +1412,22 @@  _GLIBCXX_HAS_NESTED_TYPE(result_type)
                    "Wrong number of arguments for function");
     };
 
+  template<typename _Ret, typename... _Args, typename... _BoundArgs>
+    struct _Bind_check_arity<_Ret (*)(_Args......), _BoundArgs...>
+    {
+      static_assert(sizeof...(_BoundArgs) >= sizeof...(_Args),
+                   "Wrong number of arguments for function");
+    };
+
   template<typename _Tp, typename _Class, typename... _BoundArgs>
     struct _Bind_check_arity<_Tp _Class::*, _BoundArgs...>
     {
       using _Arity = typename _Mem_fn<_Tp _Class::*>::_Arity;
-      static_assert(sizeof...(_BoundArgs) == _Arity::value + 1,
-                   "Wrong number of arguments for pointer-to-member");
+      using _Varargs = typename _Mem_fn<_Tp _Class::*>::_Varargs;
+      static_assert(_Varargs::value
+		    ? sizeof...(_BoundArgs) >= _Arity::value + 1
+		    : sizeof...(_BoundArgs) == _Arity::value + 1,
+		    "Wrong number of arguments for pointer-to-member");
     };
 
   // Trait type used to remove std::bind() from overload set via SFINAE
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index b92bbf0..cecc7dc 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -89,6 +89,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// The type used as a compile-time boolean with false value.
   typedef integral_constant<bool, false>    false_type;
 
+  template<bool __v>
+    using __bool_constant = integral_constant<bool, __v>;
+
   // Meta programming helper types.
 
   template<bool, typename, typename>
diff --git a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
index 4e627cf..7810968 100644
--- a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
@@ -30,10 +30,10 @@  void test01()
 {
   const int dummy = 0;
   std::bind(&inc, _1)(0);               // { dg-error  "no match" }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1207 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1221 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1235 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1249 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1126 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1140 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1154 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1168 }
   std::bind(&inc, std::ref(dummy))();	// { dg-error  "no match" }
 }
 
diff --git a/libstdc++-v3/testsuite/20_util/bind/refqual.cc b/libstdc++-v3/testsuite/20_util/bind/refqual.cc
new file mode 100644
index 0000000..76f4f5c
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/bind/refqual.cc
@@ -0,0 +1,43 @@ 
+// Copyright (C) 2014 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++11" }
+
+#include <functional>
+#include <testsuite_hooks.h>
+
+struct X
+{
+  int f() const& { return 0; }
+  int g(int i, ...)& { return i; }
+};
+
+void
+test01()
+{
+  X x;
+  auto b = std::bind(&X::f, &x);
+  VERIFY( b() == 0 );
+  auto bb = std::bind(&X::g, &x, 1, 2);
+  VERIFY( bb() == 1 );
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/20_util/declval/requirements/1_neg.cc b/libstdc++-v3/testsuite/20_util/declval/requirements/1_neg.cc
index debcb28..cdcbdbe 100644
--- a/libstdc++-v3/testsuite/20_util/declval/requirements/1_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/declval/requirements/1_neg.cc
@@ -19,7 +19,7 @@ 
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// { dg-error "static assertion failed" "" { target *-*-* } 2201 }
+// { dg-error "static assertion failed" "" { target *-*-* } 2204 }
 
 #include <utility>
 
diff --git a/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/refqual.cc b/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/refqual.cc
index 35a66e5..4f3a64b 100644
--- a/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/refqual.cc
+++ b/libstdc++-v3/testsuite/20_util/function_objects/mem_fn/refqual.cc
@@ -24,6 +24,7 @@  struct Foo
 {
   void r()&& { }
   int l() const& { return 0; }
+  void lv(int, ...)& { }
 };
 
 void test01()
@@ -31,4 +32,5 @@  void test01()
   Foo f;
   int i = std::mem_fn(&Foo::l)( f );
   std::mem_fn(&Foo::r)( std::move(f) );
+  std::mem_fn(&Foo::lv)( f, 1, 2, 3 );
 }
diff --git a/libstdc++-v3/testsuite/20_util/make_signed/requirements/typedefs_neg.cc b/libstdc++-v3/testsuite/20_util/make_signed/requirements/typedefs_neg.cc
index aeef380..b38ffdf 100644
--- a/libstdc++-v3/testsuite/20_util/make_signed/requirements/typedefs_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/make_signed/requirements/typedefs_neg.cc
@@ -48,5 +48,5 @@  void test01()
 // { dg-error "required from here" "" { target *-*-* } 40 }
 // { dg-error "required from here" "" { target *-*-* } 42 }
 
-// { dg-error "invalid use of incomplete type" "" { target *-*-* } 1866 }
-// { dg-error "declaration of" "" { target *-*-* } 1830 }
+// { dg-error "invalid use of incomplete type" "" { target *-*-* } 1869 }
+// { dg-error "declaration of" "" { target *-*-* } 1833 }
diff --git a/libstdc++-v3/testsuite/20_util/make_unsigned/requirements/typedefs_neg.cc b/libstdc++-v3/testsuite/20_util/make_unsigned/requirements/typedefs_neg.cc
index ebfdc42..d2e11a6 100644
--- a/libstdc++-v3/testsuite/20_util/make_unsigned/requirements/typedefs_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/make_unsigned/requirements/typedefs_neg.cc
@@ -48,5 +48,5 @@  void test01()
 // { dg-error "required from here" "" { target *-*-* } 40 }
 // { dg-error "required from here" "" { target *-*-* } 42 }
 
-// { dg-error "invalid use of incomplete type" "" { target *-*-* } 1754 }
-// { dg-error "declaration of" "" { target *-*-* } 1718 }
+// { dg-error "invalid use of incomplete type" "" { target *-*-* } 1757 }
+// { dg-error "declaration of" "" { target *-*-* } 1721 }