
shunsuke wrote:
Eric Niebler wrote:
Correct. However, I can do this:
struct make_plus : function_adaptor<make_plus> { // same as before };
where function_adaptor implements the TR1 function object protocol and makes some guess about const-ref arguments -- probably that they are rvalues. It could even implement the reference_wrapper gunk to treat reference_wrapper<X> arguments as if they were X&. <snip> I think I got the idea from reading through Egg's documentation. It seems like if this sort of thing doesn't exist there, it probably should. Shunsuke?
In C++03, it is (practically speaking) impossible to overload on "rvalueness". So, Egg doesn't tell underlying objects(called LittleFunction in Egg) whether an argument is an lvalue or rvalue. A LittleFunction always regards arguments as lvalues. It might be possible to offer some protocol for overload on "rvalueness" (e.g. rvalue_wrapper) which will be turned on in C++0x, though.
I've fleshed this idea out a bit. The gist is to write a function object in a certain way and generate the TR1 function object gunk with a CRTP base. Correct handing of const/non-const refs and reference_wrapper-as-lvalue is automatic. Here's how a simple make_pair function object would look, if it were to handle rvalue/lvalue correctly (for some definition of "correctly"): #include <boost/ref.hpp> #include <boost/mpl/assert.hpp> #include <boost/fusion/tuple.hpp> #include <boost/utility/result_of.hpp> #include <boost/type_traits/is_same.hpp> #include "./tr1_function.hpp" namespace fusion = boost::fusion; struct make_pair : tr1_function<make_pair> { template<typename Sig> struct impl; template<typename This, typename A0, typename A1> struct impl<This(A0, A1)> : tr1_function_impl<A0, A1> { typedef fusion::tuple<A0, A1> result_type; result_type operator()( typename impl::param0 a0 , typename impl::param1 a1 ) const { return result_type(a0, a1); } }; }; int main() { int i = 0; int const j = 0; fusion::tuple<int, int &> t1 = make_pair()(1,i); fusion::tuple<int &, int> t2 = make_pair()(i,j); fusion::tuple<int &, int const &> t4 = make_pair()(boost::ref(i), boost::ref(j)); typedef boost::result_of<make_pair(int, int &)>::type p1; typedef boost::result_of<make_pair(int &, int const &)>::type p2; typedef boost::result_of<make_pair( boost::reference_wrapper<int> , boost::reference_wrapper<int const> )>::type p3; using boost::is_same; BOOST_MPL_ASSERT((is_same<p1, fusion::tuple<int, int &> >)); BOOST_MPL_ASSERT((is_same<p2, fusion::tuple<int &, int> >)); BOOST_MPL_ASSERT((is_same<p3, fusion::tuple<int &, int const &> >)); } This has a few advantages: * The author of make_pair doesn't need to write an exponential number of overloads to get proper rvalue/lvalue handling. That's handled in the crtp base. * Make_pair doesn't need to worry about reference_wrappers, either. * And generic code can detect if a function object F is implemented this way, and either reference-wrap lvalue arguments or else access the nested ::impl function object directly to ensure correct rvalue/lvalue argument handling. Is this an idea worth pursuing? Can we get something like this in Egg? -- Eric Niebler Boost Consulting www.boost-consulting.com #ifndef BOOST_PP_IS_ITERATING /////////////////////////////////////////////////////////////////////////////// /// \file tr1_function.hpp /// A wrapper that makes a tr1-style function object that handles const /// and non-const refs and reference_wrapper arguments, too, and forwards /// the arguments on to the specified implementation. // // Copyright 2007 Eric Niebler. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include <boost/ref.hpp> #include <boost/preprocessor/library.hpp> #ifndef MAX_ARITY # define MAX_ARITY 5 #endif template<typename T> struct normalize_arg { typedef T type; }; template<typename T> struct normalize_arg<T const &> : normalize_arg<T> {}; template<typename T> struct normalize_arg<boost::reference_wrapper<T> > { typedef T &type; }; template<typename T> struct normalize_arg<boost::reference_wrapper<T> &> { typedef T &type; }; template<typename Sig> struct normalize_signature; #define M0(Z, N, DATA) \ template<typename This BOOST_PP_ENUM_TRAILING_PARAMS_Z(Z, N, typename A)> \ struct normalize_signature<This(BOOST_PP_ENUM_PARAMS_Z(Z, N, A))> \ { \ typedef This type( \ BOOST_PP_ENUM_BINARY_PARAMS_Z( \ Z \ , N \ , typename normalize_arg<A \ , >::type BOOST_PP_INTERCEPT \ ) \ ); \ }; \ /**/ BOOST_PP_REPEAT(MAX_ARITY, M0, ~) #undef M0 struct as_function_base {}; template<typename Derived> struct tr1_function : as_function_base { template<typename Sig> struct result : Derived::template impl< typename normalize_signature<Sig>::type > { typedef typename result::result_type type; }; #define M1(Z, N, _) ((0)(1)) #define M2(R, PRODUCT) M3(R, BOOST_PP_SEQ_SIZE(PRODUCT), PRODUCT) #define M3(R, SIZE, PRODUCT) \ template<BOOST_PP_ENUM_PARAMS(SIZE, typename A)> \ typename result<void(BOOST_PP_SEQ_FOR_EACH_I_R(R, M5, ~, PRODUCT))>::type \ operator ()(BOOST_PP_SEQ_FOR_EACH_I_R(R, M4, ~, PRODUCT)) const \ { \ return result<void(BOOST_PP_SEQ_FOR_EACH_I_R(R, M5, ~, PRODUCT))>() \ (BOOST_PP_SEQ_FOR_EACH_I_R(R, M6, ~, PRODUCT)); \ } \ /**/ #define M4(R, _, I, ELEM) \ BOOST_PP_COMMA_IF(I) BOOST_PP_CAT(A, I) BOOST_PP_CAT(C, ELEM) &BOOST_PP_CAT(a, I) \ /**/ #define M5(R, _, I, ELEM) \ BOOST_PP_COMMA_IF(I) BOOST_PP_CAT(A, I) BOOST_PP_CAT(C, ELEM)& \ /**/ #define M6(R, _, I, ELEM) \ BOOST_PP_COMMA_IF(I) BOOST_PP_CAT(a, I) \ /**/ #define C0 #define C1 const #define BOOST_PP_ITERATION_PARAMS_1 (3, (1, MAX_ARITY, "./tr1_function.hpp")) #include BOOST_PP_ITERATE() #undef C0 #undef C1 #undef M1 #undef M2 #undef M3 #undef M4 #undef M5 #undef M6 }; template<typename T> struct param { typedef T const &type; }; template<typename T> struct param<T &> { typedef T &type; }; struct na; template<BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(MAX_ARITY, class A, na)> struct tr1_function_impl { #define M0(Z, N, DATA) typedef typename param<BOOST_PP_CAT(A, N)>::type BOOST_PP_CAT(param, N); BOOST_PP_REPEAT(MAX_ARITY, M0, ~) #undef M0 }; #else BOOST_PP_SEQ_FOR_EACH_PRODUCT( M2, BOOST_PP_REPEAT(BOOST_PP_ITERATION(), M1, ~) ) #endif