Boost logo

Proto :

Subject: [proto] proto-11 progress report
From: Eric Niebler (eric_at_[hidden])
Date: 2012-06-23 19:10:14

Hash: SHA1

I've made some good progress on the C++11 proto rewrite that I'd like to
share. So far, it's been a less radical shift than I expected.

Expressions vs. Grammars
Many new users are confused by the difference between terminal<int> and
terminal<int>::type. In, there is no difference. Forget the
::type. Things just work.

Custom transforms are simpler
Currently, defining a custom transform means defining a struct with a
nested impl class template of 3 parameters, correctly inheriting and
following a protocol. In the rewrite, I wanted to simplify things. Here
for instance, is how the _expr transform is defined:

    struct _expr
      : transform<_expr>
        template<typename E, typename ...Rest>
        auto operator()(E && e, Rest &&...) const
            static_cast<E &&>(e)

A custom transform is simply a struct that inherits from
proto::transform and that has an operator() that accepts an arbitrary
number of parameters. (The use of BOOST_PROTO_AUTO_RETURN is not
necessary. It simply handles the return statement, the return type, and
the noexcept clause.)

Data parameter uses a slot mechanism
In proto today, transforms take 3 parameters: expression, state and
data. As you can see from above, transforms in proto-11 take an
arbitrary number of parameters. However, that can make it hard to find
the piece of data you're looking for. Which position will it be in?
Instead, by convention most transforms will still only deal with the
usual 3 parameters. However, the data parameter is like a fusion::map:
it will have slots that you can access in O(1) by tag.

Here is how a proto algorithm will be invoked:

  int i = LambdaEval()(_1 + 42, 0, proto::tag::data = 8);
The 3rd parameter associates the value 8 with the data tag. The _data
transform returns the data associated with that tag. Additionally, you
can define you own tags and pass along another blob of data, as follows:

  int i = LambdaEval()(_1 + 42, 0, (proto::tag::data = 8, mytag = 42));

The _data transform will still just return 8, but you can use
_env<mytag_type> to fetch the 42. The third parameter has been
generalized from an unstructured blob of data to a structured collection
of environment variables. Slots can even be reused, in which case they
behave like FILO queues (stacks).

proto::callable and proto::is_callable will no longer be necessary
Much work has gone into eliminating the need for proto::callable and
proto::is_callable. The present need comes from a difference between
the proto call and make transforms. In make<X(Y)>, X is treated as a
lambda describing a type. For instance, X might be
std::vector<proto::_value>, in which case, proto::_value is evaluated
and the result (say, int) is used to instantiate vector. proto::call
doesn't do that, and that decision has caused no end of grief. In, a transform like X(Y) is evaluated by first treating X as a
lambda like make does today. After a real type has been computed, it's
safe to ask whether the new type is a transform, callable or other.
Transforms are quite simply anything that has inherited from
proto::transform. And in C++11, there is no reason to tag something as
callable or not; extended SFINAE can do the job for us. Either X()(y)
compiles or it doesn't. As a result, proto can easily and reliably
distinguish callable transforms from object transforms with no help from
the user.

As for what is not changing:

Grammars, Transforms and Algorithms
It would be wonderful if there were a more natural syntax for describing
proto algorithms rather than with structs, function objects, proto::or_,
proto::when, and friends. If there is one, I haven't found it yet. On
the up side, it means that many current proto-based libraries can be
upgraded with little effort. On the down side, the learning curve will
still be pretty steep. If anybody has ideas for how to use C++11 to
simplify pattern matching and the definition of recursive tree
transformation algorithms, I'm all ears.

For those curious to taking a peek, the code lives here:

It needs clang trunk to compile. I also had to manually fix a bug in my
gcc headers to get the tests to compile. This is still in a very raw
state, and not useful for real work yet.

- --
Eric Niebler
BoostPro Computing
Version: GnuPG v2.0.17 (MingW32)
Comment: Using GnuPG with Mozilla -


Proto list run by eric at