|
Boost : |
Subject: Re: [boost] [fusion] undocumented sequence requirement: convert
From: Joel de Guzman (djowel_at_[hidden])
Date: 2012-08-30 03:27:45
On 8/30/2012 12:59 AM, Eric Niebler wrote:
> 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?
Yes. My gut feeling is that the "convert" mechanism is either 1) broken
or 2) we are asking it to do more than it is designed for. I am not sure
which. The intent of the convert mechanism is to be able to generate
a container C from any type of sequence S. Now, in general we don't know
the exact type of C. E.g. We know that it is a vector, but what particular
type --the number of elements and element types we don't care about.
All we know is that, hey, I have a sequence S and I want to create a
vector. Fill in the blanks (elements and element types) for me. So
that is why it is tag based. So, for example, if I want to convert S
to a fusion vector, I do:
result_of::convert<tag-of-vector, S>::type
This is the generalized mechanism behind:
result_of::as_vector<S>::type
As mentioned, S can be any sequence type. It can be an MPL sequence,
it can be a vector, a list, etc. So, there is no way really to
provide more information about S other than the requirements
for a forward fusion sequence.
So, if a container C is not generally not convertible from any
arbitrary sequence S, then, it probably does not satisfy the
convertible (inventing the name for the sake of discussion)
concept. Yes, it's not really a formalized concept, but all the
built-in fusion sequences have this ability. Take a peek at the
docs (e.g. http://tinyurl.com/6xlnxd8) and notice that containers
can take in any sequence S in its constructor. E.g.:
V(s) // Copy constructs a vector from a Forward Sequence, s.
------------------
I think I am going to embark on some fusion-care. It's really not that
difficult to add the extensible sequence concepts and CPs. Apart from
that, there are other pressing matters that I need to take care of
anyway; rvalue handling comes to mind.
Regards,
-- Joel de Guzman http://www.boostpro.com http://boost-spirit.com
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk