[boost:proto] adding new tags

Suppose I needed a new tag for representing some high-level concept that I do not want to express in terms of C++ operators [it could be a get_bit(N) operation that has very different implementation when for my numbers the unserlyining implementation is a builtin type or a GMP big int] Here's my code for adding a binary operator my_plus: struct my_plus {}; template<typename Left, typename Right> proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > const make_my_plus_expr(Left& left, Right& right) { typedef proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > expr_type; expr_type that={left,right}; return proto::generate<typename Left::domain, expr_type>::make (that); } Questions: - is the above the right way, or is there some friendlier interface? - if I wanted to support the case where left and/or right are not proto expressions, it would be enough to call generate<>::make on the non proto-ized one(s) to turn them into proto::terminal, right? Maybe proto could provide a mechanism for doing this, seems common enough [might be enough to raise as_expr_if2 from proto::detail]. But clearly then people like me will ask for N up to BOOST_PROTO_MAX_ARITY. Maybe a separate CPP constant could be used to keep the number of permutations low. Or deal separately with >2 args with an mpl (or fusion) sequence. Pretty please... [btw, if I understand the code in operators.hpp, proto doesn't explicitely check that the domains of the two operands are the same. This situation doesn't seem really supported, nor desirable, and maybe code doing it wouldn't compile anyhow. But in case it did compile and do wrong things you might consider slowing down compilation and adding a check] - is it correct that all I have to do then is to either: - make my_plus disappear before evaluation (via transforms), or - provide an evaluation context with the appropriate overload for operator()(my_plus, ...). For instance the following would give to my_plus the normal meaning of '+': template<typename Left, typename Right> double operator() (my_plus, const Left& left, const Right& right) const{ return proto::eval(left, *this) + proto::eval (right, *this); } - a simple example for this would be very nice for the documentation. Best regards, Maurizio

Tough questions. You've touched on a weakness of proto. I hope to make improvements in this area after BoostCon ... Maurizio Vitale wrote:
Suppose I needed a new tag for representing some high-level concept that I do not want to express in terms of C++ operators [it could be a get_bit(N) operation that has very different implementation when for my numbers the unserlyining implementation is a builtin type or a GMP big int]
Here's my code for adding a binary operator my_plus:
struct my_plus {};
template<typename Left, typename Right> proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > const make_my_plus_expr(Left& left, Right& right) { typedef proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > expr_type; expr_type that={left,right}; return proto::generate<typename Left::domain, expr_type>::make (that); }
Questions:
- is the above the right way, or is there some friendlier interface?
An improvement to the return type would be: proto::expr< my_plus , proto::args2< typename proto::as_arg<Left>::type , typename proto::as_arg<Right>::type >
const
proto::as_arg<> has the job of wrapping proto types in ref_<>, and non-proto types in terminal<>::type.
- if I wanted to support the case where left and/or right are not proto expressions, it would be enough to call generate<>::make on the non proto-ized one(s) to turn them into proto::terminal, right? Maybe proto could provide a mechanism for doing this, seems common enough [might be enough to raise as_expr_if2 from proto::detail].
Yes, I think you're right. Some high-level wrapper like as_expr_if2 is needed. Essentially, you'll need something like: typename my_generate< typename my_domain_of<Left>::type , typename my_domain_of<Right>::type , proto::expr< /*see above*/ >
::type const
where my_domain_of<X> returns the domain of X or proto::default_domain if X if not a proto type, and my_generate<> checks that the domains are compatible (the same, or else one is the default_domain) and that the proto::expr< /**/ > is a valid expr for that domain. This is, sadly, an exercise for the reader at this point. As is extending this to N arguments.
But clearly then people like me will ask for N up to BOOST_PROTO_MAX_ARITY. Maybe a separate CPP constant could be used to keep the number of permutations low. Or deal separately with >2 args with an mpl (or fusion) sequence. Pretty please...
Well, there's unpack_expr() in proto/make_expr.hpp which unpacks a fusion sequence into a proto expression. But it's pretty half-baked at the moment. This needs work.
[btw, if I understand the code in operators.hpp, proto doesn't explicitely check that the domains of the two operands are the same. This situation doesn't seem really supported, nor desirable, and maybe code doing it wouldn't compile anyhow. But in case it did compile and do wrong things you might consider slowing down compilation and adding a check]
Line 95 in operators.hpp has the check you're looking for.
- is it correct that all I have to do then is to either: - make my_plus disappear before evaluation (via transforms), or - provide an evaluation context with the appropriate overload for operator()(my_plus, ...). For instance the following would give to my_plus the normal meaning of '+':
template<typename Left, typename Right> double operator() (my_plus, const Left& left, const Right& right) const{ return proto::eval(left, *this) + proto::eval (right, *this); }
That's right.
- a simple example for this would be very nice for the documentation.
Agreed. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric@boost-consulting.com> writes:
Tough questions. You've touched on a weakness of proto. I hope to make improvements in this area after BoostCon ...
I think proto is a very nice addition to boost, and I'm sure with a few touches to make it easier to use for applications different from what it has been designed for it can get rapidly to the point it can be submitted formally. Do you have a timeframe in mind for this?
An improvement to the return type would be:
proto::expr< my_plus , proto::args2< typename proto::as_arg<Left>::type , typename proto::as_arg<Right>::type >
const
I have quickly tried this and it doesn't seem to work: as it is gcc complains about as_arg not defining a type in boost::proto (which surely looks like it is from the headers). But indeed as_arg is the right thing here, so I'll try some more.
- a simple example for this would be very nice for the documentation.
Agreed.
Please include how to make those new constructs displayable. You need something like: struct my_plus {}; inline char const *proto_tag_name(my_plus) { return "my_plus"; } It is important that proto_tag_name is defined in the same namespace as my_plus, otherwise it won't be found (Koenig lookup). In my case my_plus is outside proto::tag and things seem fine with a mix of tags inside and outside the proto::tag namespace, but it would be nice to have this confirmed and spelled out in the docs. Best regards, Maurizio

Maurizio Vitale wrote:
Eric Niebler <eric@boost-consulting.com> writes:
Tough questions. You've touched on a weakness of proto. I hope to make improvements in this area after BoostCon ...
I think proto is a very nice addition to boost, and I'm sure with a few touches to make it easier to use for applications different from what it has been designed for it can get rapidly to the point it can be submitted formally.
Fair. This particular area certainly needs improvement. But are you also referring to your desire for something like proto::eval() to work even when you don't have an expression object to evaluate? I don't consider that a common use case.
Do you have a timeframe in mind for this?
Sometime after BoostCon. That's all I can say right now.
An improvement to the return type would be:
proto::expr< my_plus , proto::args2< typename proto::as_arg<Left>::type , typename proto::as_arg<Right>::type >
const
I have quickly tried this and it doesn't seem to work: as it is gcc complains about as_arg not defining a type in boost::proto (which surely looks like it is from the headers). But indeed as_arg is the right thing here, so I'll try some more.
Sorry, try: typename proto::result_of::as_arg<Left>::type
- a simple example for this would be very nice for the documentation. Agreed.
Please include how to make those new constructs displayable. You need something like:
struct my_plus {}; inline char const *proto_tag_name(my_plus) { return "my_plus"; }
It is important that proto_tag_name is defined in the same namespace as my_plus, otherwise it won't be found (Koenig lookup).
Sure.
In my case my_plus is outside proto::tag and things seem fine with a mix of tags inside and outside the proto::tag namespace, but it would be nice to have this confirmed and spelled out in the docs.
Yes, the tag type doesn't need to be in the proto namespace. -- Eric Niebler Boost Consulting www.boost-consulting.com

On May 9, 2007, at 12:13 PM, Eric Niebler wrote:
Maurizio Vitale wrote:
Eric Niebler <eric@boost-consulting.com> writes:
Tough questions. You've touched on a weakness of proto. I hope to make improvements in this area after BoostCon ...
I think proto is a very nice addition to boost, and I'm sure with a few touches to make it easier to use for applications different from what it has been designed for it can get rapidly to the point it can be submitted formally.
Fair. This particular area certainly needs improvement. But are you also referring to your desire for something like proto::eval() to work even when you don't have an expression object to evaluate?
No I was not thinking of that, although it seems very easy to support from a first look at the current implementation
I don't consider that a common use case.
I didn't mean to imply that you've to support all I need. That particular feature is probably better done in an application specific way. I've looked at context.hpp and it should very easy to steal from it and I think that way I can accommodate the case when values can be compile-time constants or run-time values (another one of the mails I've sent, where I have a need for two transforms for doing something that should have been simpler. I don't think you've seen it, maybe because of my clock screw-up at the time). The main reason I'm throwing my application specific needs out there is to discover whether there's a proto way to achieve what I need. If there's I'll use it, otherwise it's up to you and others in the boost core team to decide whether something not considered before is general enough for inclusion. Best regards, Maurizio

Eric Niebler <eric@boost-consulting.com> writes:
Tough questions. You've touched on a weakness of proto. I hope to make improvements in this area after BoostCon ...
Maurizio Vitale wrote:
Suppose I needed a new tag for representing some high-level concept that I do not want to express in terms of C++ operators [it could be a get_bit(N) operation that has very different implementation when for my numbers the unserlyining implementation is a builtin type or a GMP big int]
Here's my code for adding a binary operator my_plus:
struct my_plus {};
template<typename Left, typename Right> proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > const make_my_plus_expr(Left& left, Right& right) { typedef proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > expr_type; expr_type that={left,right}; return proto::generate<typename Left::domain, expr_type>::make (that); }
Questions:
- is the above the right way, or is there some friendlier interface?
There's another way to achieve the same effect by just declaring a nullary my_plus and then let the proto machinery do its job by mean of operator()(). In this case the result would be: (function (terminal my_plus expr0 expr1)) The grammar can take care that only binary applications are allowed [my_plus could even declare its arity so that this can be done in a generic way]. What are pro/cons of the two solutions? Compile-time evaluation should be the same (albeit probably more expensive at compile-time). For evaluation in contexts I think the matching becomes more difficult to express (haven't tired it, maybe it doesn't even work) in that you need to overload on function_tag and then use boost::{dis,en}able_if to control the overload set. Run-time cost should be the same. Eric, are the two approach equivalent? or you see particular advantages for one of them? Regards, Maurizio

Maurizio Vitale wrote:
Eric Niebler <eric@boost-consulting.com> writes:
Maurizio Vitale wrote:
Here's my code for adding a binary operator my_plus:
struct my_plus {};
template<typename Left, typename Right> proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > const make_my_plus_expr(Left& left, Right& right) { typedef proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > expr_type; expr_type that={left,right}; return proto::generate<typename Left::domain, expr_type>::make (that); }
Questions:
- is the above the right way, or is there some friendlier interface?
There's another way to achieve the same effect by just declaring a nullary my_plus and then let the proto machinery do its job by mean of operator()(). In this case the result would be: (function (terminal my_plus expr0 expr1))
The grammar can take care that only binary applications are allowed [my_plus could even declare its arity so that this can be done in a generic way]. What are pro/cons of the two solutions?
Right. I should have suggested that in the first place. I tend to think this approach (declaring an empty terminal and using its operator()()) is nicer because it reuses more of proto's machinery, but the two approaches are equivalent. There are cases where this approach doesn't work, however. For example, in xpressive, there is a repeat<>() function, for repeating sub-expressions. For instance: repeat<3,6>('a') will match between 3 and 6 'a' characters. In this case, "repeat" cannot be a proto terminal; it must be a function template. So I still need to make it easier to define custom "operators" like repeat() and your make_my_plus_expr(). As for "best practice," it's too early to say. We're making it up as we go. :-) -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric@boost-consulting.com> writes:
There are cases where this approach doesn't work, however. For example, in xpressive, there is a repeat<>() function, for repeating sub-expressions. For instance:
repeat<3,6>('a')
I've taken a look at repeat and the other primitives and tags are of the form: template<int V> struct a_tag { typedef mpl::int_<V> type } from the tag names it seems like they're not intended to be used in pattern matching, but rather provide an already decoded "action" to be performed. For instance the tag for repeat is called generic_quant_tag and doesn't brings 'repeat' to mind. Is that roughly correct? Would be a pattern like the following be valid? proto::unary_expr<generic_quant_tag<3,proto::_>, proto::_> Thanks, Maurizio

Maurizio Vitale wrote:
Eric Niebler <eric@boost-consulting.com> writes:
There are cases where this approach doesn't work, however. For example, in xpressive, there is a repeat<>() function, for repeating sub-expressions. For instance:
repeat<3,6>('a')
I've taken a look at repeat and the other primitives and tags are of the form: template<int V> struct a_tag { typedef mpl::int_<V> type }
from the tag names it seems like they're not intended to be used in pattern matching, but rather provide an already decoded "action" to be performed. For instance the tag for repeat is called generic_quant_tag and doesn't brings 'repeat' to mind.
Don't try to read too much into xpressive's use of proto. I began work on xpressive in 2003. Proto evolved much later, and didn't get grammars and transforms until last November. So xpressive doesn't isn't always a paragon of how to use proto, although I try. A tag is a pure compile-time encoding of the operation performed at a node in the expression tree. It can be anything, really.
Would be a pattern like the following be valid?
proto::unary_expr<generic_quant_tag<3,proto::_>, proto::_>
No. Proto::matches<> doesn't do lambda-style matching on tag types, the way it does for terminal types. It wouldn't be hard to do, but it would make proto::matches<> much slower, for little benefit. What xpressive does instead is something like: proto::and_< proto::if_< is_quant_tag< proto::tag_of< mpl::_ > > > , proto::unary_expr< proto::_, XpressiveGrammar >
And then I can test for generic_quant_tag<> in the is_quant_tag<> trait. It's a hack, but it works. -- Eric Niebler Boost Consulting www.boost-consulting.com

What xpressive does instead is something like:
proto::and_< proto::if_< is_quant_tag< proto::tag_of< mpl::_ > > > , proto::unary_expr< proto::_, XpressiveGrammar >
I think this is a good example of why my naming suggestion is not good. As I said you have in mind more uses then me. The above written as proto::all< proto::when< >, proto::unary_expr< >
is not really palatable.

Eric Niebler <eric@boost-consulting.com> writes:
Right. I should have suggested that in the first place. I tend to think this approach (declaring an empty terminal and using its operator()()) is nicer because it reuses more of proto's machinery, but the two approaches are equivalent.
There are cases where this approach doesn't work, however. For example, in xpressive, there is a repeat<>() function, for repeating sub-expressions. For instance:
repeat<3,6>('a')
But even there you could have had repeat< > return a proto::terminal and then let the generic overloading of operator()() take care of the rest. So if I interpret correctly what you're saying here and what I see in expressive a guideline could be something like: - try to declare things as terminals, unless for other reasons (like repeat) you cannot - once you have to define a more complex structure, overload operator() as well and capture the arguments this way. Clearly easier decision for unary and binary functions, because there you just inherit the rigth stuff from proto {unary,binary}_expr. A follow up question would be: if repeat instead of being a unary function (not counting the first two arguments to the template) would have needed more than two arguments (again not counting the first two) would you still have implemented it the way it is done in expressive or would you've taken the route of making repeat<> a terminal? Thanks, Maurizio

Maurizio Vitale wrote:
Eric Niebler <eric@boost-consulting.com> writes:
Right. I should have suggested that in the first place. I tend to think this approach (declaring an empty terminal and using its operator()()) is nicer because it reuses more of proto's machinery, but the two approaches are equivalent.
There are cases where this approach doesn't work, however. For example, in xpressive, there is a repeat<>() function, for repeating sub-expressions. For instance:
repeat<3,6>('a')
But even there you could have had repeat< > return a proto::terminal and then let the generic overloading of operator()() take care of the rest.
Are you sure about that? :-) -- Eric Niebler Boost Consulting www.boost-consulting.com

On May 20, 2007, at 4:37 PM, Eric Niebler wrote:
Maurizio Vitale wrote:
Eric Niebler <eric@boost-consulting.com> writes:
Right. I should have suggested that in the first place. I tend to think this approach (declaring an empty terminal and using its operator ()()) is nicer because it reuses more of proto's machinery, but the two approaches are equivalent.
There are cases where this approach doesn't work, however. For example, in xpressive, there is a repeat<>() function, for repeating sub-expressions. For instance:
repeat<3,6>('a')
But even there you could have had repeat< > return a proto::terminal and then let the generic overloading of operator()() take care of the rest.
Are you sure about that? :-)
Oh, I see now. Maurizio

On 5/8/07, Maurizio Vitale <mav@cuma.polymath-solutions.lan> wrote:
Suppose I needed a new tag for representing some high-level concept that I do not want to express in terms of C++ operators [it could be a get_bit(N) operation that has very different implementation when for my numbers the unserlyining implementation is a builtin type or a GMP big int]
Here's my code for adding a binary operator my_plus:
For us new to proto, can you give an example of your DSEL in use where my_plus is used? ie is it: int a; int b; int c = ...my_plus.... what's it look like in use? Thanks, Tony

On May 19, 2007, at 12:02 AM, Gottlob Frege wrote:
On 5/8/07, Maurizio Vitale <mav@cuma.polymath-solutions.lan> wrote:
Suppose I needed a new tag for representing some high-level concept that I do not want to express in terms of C++ operators [it could be a get_bit(N) operation that has very different implementation when for my numbers the unserlyining implementation is a builtin type or a GMP big int]
Here's my code for adding a binary operator my_plus:
For us new to proto, can you give an example of your DSEL in use where my_plus is used?
ie is it:
int a; int b;
int c = ...my_plus....
what's it look like in use?
Suppose you have objects that derives from proto::terminal, like my_int in my previous posts. Given the declarations: my_int<4> i=2; my_int<8> j=40; You want to be able to write something like: i = my_plus(i,j); Granted, you'd probably wouldn't do this for my_plus, as you have an operator+ that looks nicer. What I need the ability of defining new "functions" for is for things like sign(expr) - returns the sign of an expression mask(expr, left, right) - return EXPR with bits outside the range left..right zeroed. etc. The reason I want to represent those "high" [higher than C++ operations] concepts is that their implementation is potentially very different. For instance extracting the sign from a builtin type is not the same as extracting the sign from a fixed- point number and it is certainly not the same as extracting it from a GNU multi- precision numbers. Note that not necessarily a comparison w/ zero would do the right thing: for instance in fixed point numbers I might decide not to sign-extend after all operations (which has a cost) and thus you have to fin the right inner bit that gives you the sign. In my application (a library of fixed-point numbers) I'll have two levels where proto is used. A "concrete" level that control the user-level expressions in the way one would expect (similar to code I posted previously, like the example that allows you to make all combinations containing signed numbers to go to signed). And then I have a meta-level where you can talk about expressions in the concrete domain. For instance if proto::eval( i+j, concrete_context) gives you 42 I want: proto::eval(value(_2), meta_context(j, i+j)) [here value() would be implemented as my_plus above] to also yield 42, but: proto::eval(value(_2), meta_context(i, i+j)) would yield 10 [with a C-like overload management, other values are also possible from my library]. The reason is that the value of value(EXPR) in the meta_context is the value of EXPR given that it must be assigned to expression _1, which in the first case is j, an 8-bit quantity and in the second case is i, a 4- bit quantity. In the process of evaluating value(_2) another meta-expression will be evaluated: mask(value(_2), left(_1)-left(_2)) which does the masking when needed. The goal is to have the equivalent of the following in the assembly code: R1 <- i R2 <- j R1 <- R1 + R2 R1 <- R1 & 0xf i <- R1 for the first case and to skip the masking all together for the second case. It would take longer to go into the details of the fixed-point application, but I hope the above gives you an idea of why I needed something like 'my_plus'. It takes some time and effort to understand how proto works, but if you have an application that fits it is time well spent. Regards, Maurizio

Maurizio Vitale wrote:
Suppose I needed a new tag for representing some high-level concept that I do not want to express in terms of C++ operators [it could be a get_bit(N) operation that has very different implementation when for my numbers the unserlyining implementation is a builtin type or a GMP big int]
Here's my code for adding a binary operator my_plus:
struct my_plus {};
template<typename Left, typename Right> proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > const make_my_plus_expr(Left& left, Right& right) { typedef proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > expr_type; expr_type that={left,right}; return proto::generate<typename Left::domain, expr_type>::make (that); }
Questions:
- is the above the right way, or is there some friendlier interface?
You can now do this with make_expr. It would look like this: struct my_plus {}; proto::functional::make_expr<my_plus> const make_my_plus_expr = {}; Then you can say make_my_plus_expr(e1, e2) and get a binary expr with my_plus as a tag type. If either e1 or e2 are not yet a proto expression, they are made into proto terminals first. If you want to specify a domain, so the resulting expr is wrapped in some extension wrapper, you can specify the domain as the second template parameter, as struct my_plus {}; proto::functional::make_expr<my_plus, my_domain> const make_my_plus_expr = {}; Currently, the domains of the children expressions are *not* considered in either case. This seemed simpler. Let me know if this doesn't meet you needs. I can imagine using a pseudo-domain for the second parameter that means: propagate the domain from the children nodes. -- Eric Niebler Boost Consulting www.boost-consulting.com
participants (4)
-
Eric Niebler
-
Gottlob Frege
-
Maurizio Vitale
-
Maurizio Vitale