Boost logo

Boost :

Subject: Re: [boost] [fusion] undocumented sequence requirement: convert
From: Eric Niebler (eric_at_[hidden])
Date: 2012-08-29 12:59:57


On 8/29/2012 12:58 AM, Joel de Guzman wrote:
> On 8/28/2012 2:06 PM, Eric Niebler wrote:
>> IMO, this is less than ideal. In this case, it so happens that tag and
>> domain are pure compile-time information, and can be bundled with the
>> fusion tag. Generally, that won't be the case. Any type that models a
>> more refined concept than Fusion[xxx]Sequence can have additional
>> requirements that would be effectively sliced off by round-tripping
>> through this customization point.
>>
>> Joel, do you have thoughts about this? Would extensible Fusion sequences
>> solve this?
>
> In my mind, if we have forward and backward extensible Fusion sequences
> (and we should), the proper CP will be fusion::pop_back_impl. Fusion's
> mpl extensions, e.g. mpl::pop_back_impl will simply call fusion's
> CP for known (traits) forward and backward extensible Fusion sequences
> and default to the original behavior for non-extensible Fusion sequences
> (as before).

Makes sense.

> Convert is another story. And as you pointed out, it looks like it
> needs more info than is provided in the CP. But still I lack proper
> understanding of this particular use-case. In general, convert should
> be able to convert any fusion sequence T (including MPL sequences)
> to another sequence U.

The problem was reported by Mathias Guarnard and reported here:
<https://svn.boost.org/trac/boost/ticket/7254>

He was calling mpl::pop_back on a Proto expression and it was failing.
Here is the code, which fails on 1.51 but will succeed on trunk because
of my ugly hack:

#include <boost/proto/proto.hpp>
#include <boost/fusion/mpl.hpp>
#include <boost/mpl/pop_back.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/static_assert.hpp>
namespace mpl = boost::mpl;
namespace proto = boost::proto;
namespace fusion = boost::fusion;
using proto::_;

template<class E>
struct my_expr;

struct my_domain
  : proto::domain<proto::generator<my_expr> >
{};

template<class E>
struct my_expr
  : proto::extends<E, my_expr<E>, my_domain>
{
    my_expr(E const &e = E())
      : proto::extends<E, my_expr<E>, my_domain>(e)
    {}

    typedef fusion::fusion_sequence_tag tag;
};

template<typename T>
void test_impl(T const &)
{
    typedef typename mpl::pop_back<T>::type result_type;
    BOOST_STATIC_ASSERT(
        (boost::is_same<
            result_type
          , my_expr<proto::basic_expr<proto::tag::plus,
proto::list1<my_expr<proto::terminal<int>::type>&> > >
>::value)
    );
}

// Test that we can call mpl algorithms on proto expression types, and
get proto expression types back
void test_mpl()
{
    my_expr<proto::terminal<int>::type> i;
    test_impl(i + i);
}

The MPL tag of the Proto expression is fusion_sequence_tag, so
mpl::pop_back uses fusion's mpl hooks. The fusion implementation of mpl
pop_pack first creates a fusion iterator_range of fusion pop_back
iterators, both of which wrap a proto expression iterator. Then, fusion
calls convert to turn this fusion sequence back into a proto expression
before handing it back to mpl.

Proto, in the convert_impl CP, just sees a fusion range of proto
expressions coming in. It can't know in general what proto expression
was initially used before fusion modified it, so it can't find out what
that expression's tag type was. And it needs to know that if it's to
turn the sequence of child expressions back into an expression.

Does that help?

-- 
Eric Niebler
BoostPro Computing
http://www.boostpro.com

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