|
Proto : |
Subject: Re: [proto] Thoughts on traversing proto expressions and reusing grammar
From: Eric Niebler (eric_at_[hidden])
Date: 2010-10-16 18:51:00
On 10/16/2010 12:15 PM, Thomas Heller wrote:
> On Saturday 16 October 2010 03:45:45 Eric Niebler wrote:
>> On Fri, Oct 15, 2010 at 1:35 AM, Thomas Heller
>>
>> <thom.heller_at_[hidden]>wrote:
>>> On Wednesday 13 October 2010 07:10:15 Eric Niebler wrote:
>>>> I had a really hard time grokking unpack. In Proto, function-types
>>>> are used to represent function invocation and object construction.
>>>> The type "X(A,B,C)" means either call "X" with three arguments, or
>>>> construct "X" with three arguments. In "unpack< X(A, B, C) >" X does
>>>> *not* get called with three arguments. It gets called like "X(
>>>> A(_0), A(_1), A(_2), ...B, C )", where _0, _1 are the child nodes of
>>>> the current expression. This will confuse anyone familiar with
>>>> Proto. I'd like to replace this with something a little more
>>>> transparent.
>>>
>>> Ok, i put to together an alternative version. This hooks onto the
>>> already in
>>> place transform mechanism and works like this:
>>> Whenever the first argument to a transform is proto::vararg<Fun> it
>>> expands the child expressions and applies the Fun transform before the
>>> actual transform gets called, example:
>>>
>>> struct test
>>> : proto::when<
>>> proto::function<vararg<proto::_> >
>>> , foo(vararg<proto::_>)
>>> >
>>> {};
>>>
>>> this will call foo(a0, a1 ... aN) where aX are the children of the
>>> function expressions. Note that you can replace proto::_ by any
>>> transform you like. Thus making the call look like:
>>> foo(Fun(a0), Fun(a1), Fun(a2) ...)
>>
>> Ah, clever! I like it. I'd like to see this generalized a bit though. For
>> instance, if the transform were instead "foo(_state, vararg<_>)" the
>> intention is clear, but it doesn't sound like your implementation would
>> handle this.
>
> Well, it could be done. Given that vararg expands to all children of the
> current expression,
Then IMO vararg is a really bad name. It's non-descriptive. The C++0x
term for unpacking a parameter pack is "pack expansion". That's
analogous to what this feature does, so IMO "unpack" or "expand" are the
logical choices.
> this is possible. Though it would add to compile time.
> The current implementation is in O(N^2),
Why?
> allowing to place vararg anywhere
> in the transform call is tempting, but it would introduce O(N^3)
> preprocessor iterations.
We should apply some big brains to see if there's a way to accomplish
this efficiently, maybe with a more efficient PP implementation and/or
function overloading. The last time I ran into a brick wall like this, I
posed it as a programming challenge to boost-devel and got several
really creative solutions. There are some smart folks out there.
> Let me add here, that i am planning on writing a boost.wave driver which
> allows to preprocess headers (as MPL already does), expanding BOOST_PP
> iteration constructs until a certain N. I believe this kind of preprocessing
> will bring down compile times of proto, fusion and phoenix. (There must be a
> reason why MPL does it, right?)
Very cool!
>> Actually, the semantics get somewhat tricky. And I fear it
>> would lead folks to believe that what gets unpacked by the vararg in the
>> transform corresponds exactly to the children that were matched by the
>> vararg in the grammar. That's not quite the case, is it?
>
> No, that is not the case. Let me remind you of the implementation of the
> pass_through transform for nary_expr or function. The semantics are almost
> exactly the same. The little difference you have with vararg being part of
> pass_through is that vararg is the last argument, expanding to the remaining
> children.
Correct. So if you have:
when<
function< This, vararg<That> >
, call_this(vararg<That>)
>
you have a major whoops on your hands. Why? Because it will pass to That
something that matched This, which violates That's preconditions. You
really need to handle this case.
With unpack as I suggested it:
when<
function< This, vararg<That> >
, call_this(unpack(pop_front(_), That))
>
But I think a promising line of attack would be to study the syntax of
pack expansion and pack expansion patterns, and make it as similar as
possible. No concrete suggestions yet.
>> How about this ... an "unpack" placeholder that works essentially how
>> your vararg does, but with any Fusion sequence (including Proto
>> expressions), and the sequence has to be explicitly specified (like the
>> first parameter to proto::fold).
>>
>> So, instead of foo(vararg<_>), you'd have foo(unpack(_)), where _ means
>> "the sequence to unpack", not "the transform to apply to each element".
>> If you want to apply a transform to each element first, you could
>> specify the transform as a second parameter: foo(unpack(_, Fun)). (That
>> form would probably use fusion::transform_view internally.)
>>
>> The implementation of this would be very tricky, I think, because unpack
>> could appear in any argument position,not just the first. And you'd have
>> to be careful about compile times. But a fun little problem!
>
> This sounds interesting as well, but i don't think adding another
> placeholder really helps people in that case.
Have I changed your mind?
>>> Additionally, you can add whatever arguments you like to the transform.
>>
>> Which transform? Example?
>
> The foo transform. You can call it with whatever arguments you like, just
> like a regular transform.
I'm looking at this:
struct test
: proto::when<
proto::function<vararg<proto::_> >
, foo(vararg<proto::_>)
>
{};
In this, foo doesn't look like a transform. It looks like an ordinary
callable function object. And if it were a transform, I don't see where
the transform arguments would go. Still confused.
-- Eric Niebler BoostPro Computing http://www.boostpro.com
Proto list run by eric at boostpro.com