Boost logo

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