|
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
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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk