On 11/8/2010 3:36 PM, Luc Danton wrote:
Hello everyone,
I've been meaning to learn to use Boost.Proto for quite some time and I've finally started getting my hands dirty in code ever since that fantastic video from BoostCon 2010. After the usual exercises that are presented in it and the docs, I've set up my own goal of trying to write composable futures (something that I think quite a lot of people have been doing in their own time).
That's an ambitious project for your first outing! Proto has a futures example that you may have seen (libs/proto/example/futures.cpp), but it doesn't address your concerns below.
You can see the code at http://pastebin.com/DW4LckMP and I'd welcome comments on what I've been doing wrong and how I could improve the design. I also welcome answers to my following questions:
I've come to really dislike Proto's contexts. I personally prefer grammars+transforms.
* regarding copying, the docs present the following example: auto sum = boost::proto::lit(1) + 2; here, since the 1 is held by a reference which will outlive (in the variable sum) the actual literal expression (in the call to proto::lit) we will have UB.
Correct. Expression templates and "auto/decltype" don't co-exist happily. Much care needs to be taken if storing these things in local variables is part of your usage requirements.
To avoid this exact problem, and since Boost.Proto is not move aware and std::future is move-only, my terminals are actually of the form:
future_expresion
std::future&::type> (where future_expression is the expression wrapper). Can I do this? Obviously I still have a problem if I do:
std::future<int> fint = ... auto sum = ns::compose(fint) + 3; // then use sum.get()
don't I?
Yes.
I can't solve it by using proto::deep_copy since std::future is move-only (apparently my terminals get stripped of their reference). This means that the 'correct' way of doing the above would be
std::future<int> fint = ... auto const operand = 3; auto sum = ns::compose(fint) + operand; // use sum.get()
thus separating lifetimes from computations (expression trees, really).
And that doesn't solve the problem completely, because intermediate expression nodes are also held by reference. Expressions any larger than this will be UB. <snip>
Any idea?
As you have learned, Proto is not move-aware. My hand-wavy solution would be to (a) be sure that everything is held by value in an ET. With 1.42, that's by using proto::by_value_generator. In 1.44, the preferred solution is with a domain-specific as_child implementation. And (b) to wrap move-only types in a "copyable" type that, in its copy ctor, moves the wrapped value. I'll see if I can come up with an example that demonstrates. -- Eric Niebler BoostPro Computing http://www.boostpro.com