Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2008-03-27 14:48:58

Noah Stein wrote:
> My first stab at forming a vector type was, roughly:
> template<typename T>
> class vector
> : vector_expr< typename proto::terminal< vector<T> >::type >
> This fails to compile.

Ah, I see now.

> So it would appear that the terminals of my
> expressions cannot be terminals in expressions (so much for clarity in this
> post!). Thus I need to split my class into two parts: one that stores the
> data and one that defines the DSEL. I guess my lack of understanding in this
> case boils down to the question: Why isn't a lazy_vector a terminal in
> expressions composed of lazy_vectors? I don't see the difference
> conceptually.

Well, first let me address the compile error by analyzing what the above
code means, piece by piece:

   typename proto::terminal< vector<T> >::type

This is a proto terminal that holds a vector<T> by value. That is,
inside this terminal there is a vector<T> object.

   vector_expr< typename proto::terminal< vector<T> >::type >

As defined previously, vector_expr<E> "extends" the expression E. It
behaves like an E and, as far as Proto is concerned, it *is* an E.
vector_expr<E> holds an E object by value.

   template<typename T>
   class vector
     : vector_expr< typename proto::terminal< vector<T> >::type >

This says that vector<T> is ... something that holds a vector<T> by
value. That's clearly not going to work.

The larger question is, should it be possible to define a type that is a
proto terminal, but that doesn't extend some other proto terminal? That
would be a good thing because it wouldn't force you to separate your
data from your user-visible types. Another way to say this is, can I
non-intrusively make a type a proto terminal?

The answer is yes. There is an (undocumented) way to non-intrusively
make non-proto types behave like proto terminals. Consider:

     #include <boost/proto/proto.hpp>
     using namespace boost;
     using namespace proto;

     template<typename T>
     struct vector
         // vector impl here

     template<typename T>
     struct is_vector : mpl::false_ {};

     template<typename T>
     struct is_vector<vector<T> > : mpl::true_ {};

     BOOST_PROTO_DEFINE_OPERATORS(is_vector, default_domain)

     int main()
         vector<int> vi;
         // OK, vi is a proto terminal!
         // This builds a proto expression tree:
         vi + 32;

The operator overloads that Proto defines requires at least one operand
to be either expr<> or some type that actually extends expr<>. If
neither is true for your terminal, as is the case with vector<T> above,
you'll need to define your own operator overloads (with
BOOST_PROTO_DEFINE_OPERATORS) and provide a Boolean metafunction that
can be used to contrain the overloads (e.g., is_vector<>).

> I definitely see the potential for handling so many domains. After iterating
> a few times to figure out how to handle expression trees properly for a
> single domain, I definitely saw the value in a general-purpose system. It's
> a gigantic step from a single custom-written system to a general system. I'm
> very excited to get to the stage where I can play around with transforms as
> expression optimization was something I wanted to play with before, but I
> just hadn't gotten around to it yet.
> The vector class was what I figured would be the gentlest introduction to
> your library, seeing as I've already written an expression tree handler for
> it.

Right, and it turned out to be not-so-gentle because Proto does things

> There are a lot of uses for proto that I see. I'd like to try adapting
> phoenix to create a library that implements co-routines. There are some
> interesting possibilities for state machines. I just need more time!

Me too.

Eric Niebler
Boost Consulting

Boost list run by bdawes at, gregod at, cpdaniel at, john at