Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2008-03-26 11:55:23


Noah Stein wrote:
> Eric,
>
> I spent a few hours the other week trying to convert my last matrix-vector
> class library over to proto. It implemented expression trees, but I’d never
> gotten around to adding some features that proto should make much, much
> easier to implement such as expression optimization. I figured it would be a
> good way to experiment with the library to review. I eventually gave up on
> refactoring my class towards proto and instead tried refactoring your
> lazy_vector example towards my class. It's raised some questions/points.
> I'll illustrate with questions that are based on your lazy_vector example.
>
> 1) lazy_vector seemed like it could derive from lazy_vector_expr in CRTP
> fashion. I figured your use of std::vector<T> as the terminal type was just
> to make initialization easier;

??? It's a lazy_vector. My use of std::vector<> has nothing to do with
initialization. It's to hold a vector of elements.

> however, using CRTP fails to compile. Did I
> miss this in the documentation or is it not explicitly mentioned?

What failed to compile? Can you show me the code?

> 2) How can I handle scalar values transparently? My attempts to accept
> "v1=v2*5" have failed. I can't get the 5 to implicitly construct into a
> scalar_vector type I have defined. I had that working in my old one, but I
> can't seem to figure out the correct way of setting that up in proto.

In proto, literals like 5 are converted to terminal<int>::type by
default. It sounds like what you want is for int terminals to be treated
as a vector of scalars. There are a couple of ways to approach this.

1) At some point, you'll need to evaluate the expression, perhaps with
proto::eval() and a custom context class. In your context, you can add a
handler for int terminals and treat them like scalar vectors. This is
what I would do.

2) Before you evaluate an expression that may contain int terminals, you
can apply a proto transform that replaces all int terminals with scalar
vectors. This is probably not the ideal solution for you, unless you
need to do other transforms, too (like expression optimization).

3) You can cause int terminals to be converted into scalar vectors on
expression construction by using a custom generator. Think of a
generator as a very simple transform that is applied to every new
expression that proto creates for you. In the lazy_vector example, the
lazy_vector_domain is defined like this:

// Tell proto that in the lazy_vector_domain, all
// expressions should be wrapped in laxy_vector_expr<>
struct lazy_vector_domain
   : proto::domain<
         proto::generator<lazy_vector_expr>
       , LazyVectorGrammar
>
{};

proto::generator<> is a simple generator that takes any expression type
and wraps it in lazy_vector_expr<>. You could write one that first turns
scalar terminals into scalar vectors, and then wraps them in
lazy_vector_expr<>.

(3) is workable, but I would discourage it (see below). (2) is the Proto
Way (tm), but might be tricky for someone who doesn't yet know
transforms. (1) is also the Proto Way (tm) and might be the simplest
thing to do.

> 3) My vector class' signature is very simple: template<typename T> class
> Vector : VectorExpr<terminal<T> > .

You probably mean VectorExpr<terminal<T>::type>, right?

> The type T contains a number of traits
> defining the vector: the type of the elements, the dimension, how the data
> is stored. I can't figure out how to specify the terminals in my grammar. In
> your example, you use terminal<std::vector<_> > as your terminal. The
> std::vector part is unknown to me. That's my point of variation in my vector
> concept thus I can't know all the different types that could be used in the
> terminal. I tried deriving my storage classes from a common base class;
> however, that failed. Is there any way to encode some sort of restriction
> in the grammar for this situation? Right now, I'm using _, but that's
> clearly not a great solution.

If you have derived your storage classes from a common base, then try
this to match your terminals:

   proto::and_<
       proto::terminal<_>
     , proto::if_< is_base_and_derived< common_base, _arg >() >
>

> Proto looks very interesting. I was quite excited the first I saw it
> mentioned. There seems to be a lot of power there. I just need to figure out
> how to use it. You have a lot of documentation, but it seems to be attacking
> the problem from the opposite direction I'm approaching from, so it's been a
> little slow going. I hate asking questions about something I can investigate
> on my own, but I'd like to get far enough along that I can properly review
> it.

Yes, Proto comes at the problem differently than other expression
template libraries, because it's not really an expression template
library. It's a library for building domain-specific languages. In
Proto, the expression template itself, and the operator overloads that
build it, are rather incidental. The focus of Proto is what you *do*
with the expression once you have it. So rather than asking, "How can I
get Proto to build the type I want," you might ask, "How can I get the
behavior I want from the types Proto builds." The answer for that is
usually either: (a) write a context, or (b) write a transform.

While you can certainly get Proto to build you the type that you want,
that's not Proto's natural mode of operation. By default, Proto builds
an abstract tree representing the expression, with little or no
domain-specific information in it. All the domain-specific knowledge is
centralized in your domain's contexts or transforms. The reason I did it
this way was to facilitate cross-domain interoperability. The same
expression can mean one thing in one domain and something else in a
different one. That allows expressions from different domains to be
intermingled freely, as with semantic actions (lambda domain) in a
grammar (parser domain). Or consider the placeholder terminal _1. You
would like that to mean different things to bind, lambda, spirit, karma,
phoenix, xpressive, etc. If it were a proto terminal, all these
libraries could use the same terminal -- it's just syntax -- and the
semantics could vary by library.

I hope this gets you a little farther along.

-- 
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