
Eric Niebler wrote:
Steven Watanabe wrote:
users_guide\expression_transformation\is_callable.html: ...a problem for a type like std::vector<_arg(_arg1)>(), which is a valid transform... Can't you substitute /first/ and /then/ check is_callable?
I could do that, yes. And I seem to recall having a reason for not doing that.
... time passes ...
Either the reason escapes me, or else my reasoning was wrong. I'll investigate this.
In trying to implement this, I reminded myself of why it doesn't work. Take the case of wanting to use functional::make_expr<> as a transform. I'll use as my example a variation of the Distribute transform I wrote for Markus Werle. using functional::make_expr; struct Distribute : when< or_<plus<_,_>, minus<_,_> > , make_expr<tag_of<_> >( Recurse(_left) , Recurse(_right)) ) > {}; The idea is to match a plus or a minus and create a new node *of the same type* with transformed children. The above transform doesn't work as-is today. The reason is because Proto first checks is_callable<make_expr<tag_of<_> > >::value before doing anything. It's true, and so we never substitute tag_of<_> before invoking the make_expr<> transform. You might say, "well, don't check is_callable<> first. Just do the substitution and check after." There are two things wrong with that. The first is that it is slow. Every template type must be completely disassembled and reassembled before you know what to do with it. But the killer is that in some cases it, it is simply wrong. Often I pass one transform as a template parameter to another. For instance, foo<_arg> might be a transform that first applies the _arg transform, and then does foo on it. If Proto actually replaced _arg with an expression before invoking the foo<> transform, it would be an error! Another way to see the problem is to consider that, within a transform, the following should logically behave identically: typedef foo<_arg> Foo; struct Foo : foo<_arg> {}; These are two equivalent ways of defining some transform Foo today. If I took the "substitute first, ask questions later" approach, these would mean different things, because the _arg in the first line would be subject to substitution, but the _arg in the second would not. You might then say, "well, the problem is that is_callable<make_expr<X>
::value was true. It should be false." But no, that's obviously wrong because make_expr<> really *is* callable. Saying it's not would break make_expr<tag::plus> ...
You can chase the reasoning to all sorts of strange places. is_callable<X> should rip apart templates and examine their constituents to determine the result -- but that breaks things too, like the foo<_arg> example which should be invoked as-is without substitution. It comes down to this ... Proto can't know whether you want substitution first or not, so it errs on the side of doing the simple and efficient thing. If you want to do substitution first and *then* invoke the resulting callable transform, you can use bind: using functional::make_expr; struct Distribute : when< or_<plus<_,_>, minus<_,_> > , bind< make_expr<tag_of<_> >( Recurse(_left) , Recurse(_right)) ) > > {}; (I think bind may be incorrectly named. I may call it "lazy".) All this belongs in a rationale, if only so that I don't forget it again. -- Eric Niebler Boost Consulting www.boost-consulting.com