
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 differently.
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 www.boost-consulting.com