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.
commit 07591983a7880e2370fbc10020e0d9cdfaa86678 Author: Jonathan Wakely <jwak...@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 }