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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk