|
Boost : |
From: Tobias Schwinger (tschwinger_at_[hidden])
Date: 2006-12-20 19:03:58
Christian Holmquist wrote:
> Tobias,
> Thanks you for the reply on this matter.
>
> Is your code available in the Boost Vault for download, and some example
> using it?
>
There used to be an early stage of both in the vault, but I took it offline once it was clear to me that it was still very much insufficient. I can only find the example code ITM and will attach it to this post.
>>There would be multiple variants of these class templates with different
>>properties of the call operator:
>
>> - only accepting lvalues (like make_<seq>)
>> - only accepting rvalues (like <seq>_tie)
Sorry, here's a typo - <seq>_tie binds lvalues and make_<seq> binds to rvalues, of course.
>> - only accepting predefined types (like <seq>'s ctor, this one would
>> have another template parameter)
>> - accepting any combination of lvalues and rvalues (exploding all 2^N
>> possibilities - a brute force solution to the forwarding problem,
>> until we have better ways to solve it)
>
> I don't understand your reason for the lvalues/rvalues explotion. The
> distinct type is available in the fusion sequence, why overload completely
> unspecified types? (if that's what you do?).
<quote from your original post>
template<class A0, class A1, ....>
void operator()(const A0& a0, const A1& a1, ...) const
{
do_something_very_clever(fusion_sequence_type(a0, a1, ...));
}
</quote from your original post>
Here we don't know the template argument types, do we? It's the first case I mentioned (the most interesting cases are those where 'fusion_sequence_type' isn't known before operator() is instantiated).
Although (almost) everything binds to "T const &" we can't know whether it was a temporary, so we can't safely store the refernce - it might start dangling.
BTW: The result type is 'void'. If we want to factor this code out into some generic utility it's probably a wise choice to allow different results.
The second case I mentioned is:
template<class A0, class A1, ....>
whatever operator()(A0& a0, A1& a1, ...) const;
Now temporary arguments are not allowed at all (which can be very impractical of an interface) - a "pure tie".
The fourth case lets us detect whether something is not a temporary, which can be a very valuable thing, especially if we want to defer function invocations.
For an in-depth discussion of the issue see
http://tinyurl.com/6enrw - The Forwarding Problem - Dimov, Hinnant, Abrahams
> Given the sequence<int, int&, const int&> I would expect the overload to be
> (ignoring return type here) void operator()(int, int&, const int&).
In this case we are talking about the third case (the one where we know the types).
> I assume I've oversimplified the issue, so what aspect am I missing here?
Never mind - I did so two times. I'm talking about how things should be done and not about I did them, after all.
Regards,
Tobias
// (C) Copyright Tobias Schwinger
//
// Use modification and distribution are subject to the boost Software License,
// Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt).
//------------------------------------------------------------------------------
//
// A simple bind implementation (compiles with msvc >= 7.1)
//
#include <typeinfo>
#include <iostream>
#include <boost/fusion/sequence/container/vector/vector.hpp>
#include <boost/fusion/sequence/intrinsic/front.hpp>
#include <boost/fusion/sequence/intrinsic/value_at.hpp>
#include <boost/fusion/sequence/intrinsic/at.hpp>
#include <boost/fusion/sequence/utility/unpack_args.hpp>
#include <boost/fusion/algorithm/transformation/transform.hpp>
#include <boost/fusion/algorithm/transformation/pop_front.hpp>
// "case 1" from the post NOTE: NOT PART OF FUSION
#include <boost/fusion/sequence/generation/vector_generator.hpp>
// this header defines an experimental component that allows to specify the
// result of a Fusion function object with an MPL lambda expression
// NOTE: NOT PART OF FUSION
#include <boost/fusion/support/lambda_result.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/type_traits/add_const.hpp>
#include <boost/type_traits/add_reference.hpp>
// needed to workaround unpack_args bug
#include <boost/fusion/sequence/conversion/as_vector.hpp>
// note: another bug in unpack_args keeps this binder from working with
// non-class functors.
namespace impl
{
// note: one character identifiers keep qualified names short
namespace r = boost::fusion; // r for runtime code
namespace m = boost::fusion::result_of; // m for metafunctions
namespace mpl = boost::mpl;
using namespace mpl::placeholders;
// use runtime code by default,
using namespace r;
// metafunctions unlikely to clash can go here
using boost::add_const;
using boost::add_reference;
// placeholder template
template<int I> struct placeholder
: mpl::int_<I>
{ };
// is T the type of a placeholder?
template<typename T> struct is_placeholder
: mpl::false_
{ };
template<int I> struct is_placeholder< placeholder<I> >
: mpl::true_
{ };
// runtime code for the transform operation
template<typename Args> class transform_op
: public
lambda_result
<
mpl::if_< is_placeholder<_2>, m::at< add_const<_1>, _2>
, add_reference< add_const<_2> > >
>
::template closure<Args>
{
Args const & ref_args;
public:
explicit transform_op(Args const & args)
: ref_args(args)
{ }
// return bound argument
template<typename BindingArg>
inline BindingArg const & operator()(BindingArg const & a) const
{
return a;
}
// substitute placeholders with elements from the arguments tuple
template<int Index>
inline typename m::at_c<Args const, Index>::type
operator()(placeholder<Index> const &) const
{
return at_c<Index>(this->ref_args);
}
};
// bound_function's operator() contains the argument transformation and
// invokes the function
template<class Binding> class bound_function
: public
lambda_result
<
m::unpack_args< m::value_at< add_const<_1> , mpl::int_<0>::type>
,
m::as_vector< // <-- workaround for unpack_args bug
m::transform< m::pop_front< add_const<_1> >
, transform_op<_2>
>
> // <-- workaround for unpack_args bug
>
>
::template closure<Binding>
{
Binding tpl_binding;
public:
explicit bound_function(Binding const & binding)
: tpl_binding(binding)
{ }
template<typename Args>
inline typename result<Args>::type
operator()(Args const & args) const
{
return unpack_args( front(this->tpl_binding)
,
as_vector( // <-- workaround for unpack_args bug
transform( pop_front(this->tpl_binding)
, transform_op<Args>(args)
)
) // <-- workaround for unpack_args bug
);
}
typedef bound_function type;
// we leave this out for the sake of simplicity -- a full bind is not
// possible...
typedef mpl::false_ accepts_nullary_sequence;
};
// vector_generator is used to create a bound_function
struct bind_op
:
lambda_result< vector_generator< bound_function<_> > >
{
template<typename Binding>
inline typename result<Binding>::type
operator()(Binding const & binding) const
{
return typename result<Binding>::type(bound_function<Binding>(binding));
}
};
typedef vector_generator<bind_op, mpl::false_> binder;
}
// placeholder globals
impl::placeholder<0> const _1_ = impl::placeholder<0>();
impl::placeholder<1> const _2_ = impl::placeholder<1>();
impl::placeholder<2> const _3_ = impl::placeholder<2>();
impl::placeholder<3> const _4_ = impl::placeholder<3>();
// the binder is a global variable with an overloaded call operator
impl::binder const bind = impl::binder();
//// example code
struct func
{
typedef int result_type;
inline int operator()(char const * greeting, char const * name) const
{
std::cout << greeting << ' ' << name << '!' << std::endl;
return 77;
}
};
int main()
{
func f;
bind(f,"Hello",_1_)("Phoenix");
return 0;
}
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk