Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2008-04-06 04:48:52

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

     typedef boost::result_of<make_pair(int &, int const &)>::type

     typedef boost::result_of<make_pair(
           , boost::reference_wrapper<int const>

     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

    /// \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

    #include <boost/ref.hpp>
    #include <boost/preprocessor/library.hpp>

    #ifndef MAX_ARITY
    # define MAX_ARITY 5

    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( \
                Z \
              , N \
              , typename normalize_arg<A \
              , >::type BOOST_PP_INTERCEPT \
            ) \
        ); \
    }; \
    #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 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) \

        #define M5(R, _, I, 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;
    struct tr1_function_impl
        #define M0(Z, N, DATA) typedef typename param<BOOST_PP_CAT(A, N)>::type BOOST_PP_CAT(param, N);
        #undef M0




Boost list run by bdawes at, gregod at, cpdaniel at, john at