|
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