[fusion] value_of tranform_view

Hi, cvs head can't compile this. #include <boost/fusion/include/as_vector.hpp> #include <boost/fusion/include/transform_view.hpp> #include <boost/fusion/include/vector.hpp> struct identity { template<class FunCall> struct result; template<class Fun> struct result<Fun(int&)> { typedef int& type; }; int& operator()(int& i) const { return i; } }; int main() { typedef boost::fusion::vector<int, int> from_t; from_t from; boost::fusion::transform_view<from_t, ::identity> v(from, ::identity()); // error. boost::fusion::as_vector(v); } fusion::as_vector calls result_of::value_of. Then, <boost/fusion/sequence/view/transform_view/detail/value_of_impl.hpp> calls result_of::value_of. So, non-reference int is passed to ::identity::result. I'm not sure which is wrong, as_vector or transform_view. Regards, -- Shunsuke Sogame

shunsuke wrote:
Hi,
cvs head can't compile this.
#include <boost/fusion/include/as_vector.hpp> #include <boost/fusion/include/transform_view.hpp> #include <boost/fusion/include/vector.hpp>
struct identity { template<class FunCall> struct result;
template<class Fun> struct result<Fun(int&)> { typedef int& type; };
int& operator()(int& i) const { return i; } };
int main() { typedef boost::fusion::vector<int, int> from_t; from_t from; boost::fusion::transform_view<from_t, ::identity> v(from, ::identity());
// error. boost::fusion::as_vector(v); }
fusion::as_vector calls result_of::value_of. Then, <boost/fusion/sequence/view/transform_view/detail/value_of_impl.hpp> calls result_of::value_of. So, non-reference int is passed to ::identity::result. I'm not sure which is wrong, as_vector or transform_view.
Confirmed. It seems 'transform_view' needs a 'remove_reference' somewhere. There might be other places with similar issues. I'll check on it tomorrow. It's wrong to have the signature for result_of contain references: The result_of specification says the parameter types in the signature type should be interpreted as types of L-Value arguments used in a call expression (cited from memory). To understand why this strategy makes sense let's consider a simple call site: identity i; int j; result_of<identity(int)>::type k = i(j); It should compile fine. I can't (and don't want to) know that the L-Value 'j' is bound to a reference! Even if the argument actually /is/ a reference it doesn't make much sense to tell result_of about it - it makes no difference for the outcome and just makes nested 'result' metafunctions tedious (if not impossible) to implement. Regards, Tobias

On 7/12/07, Tobias Schwinger <tschwinger@isonews2.com> wrote:
It's wrong to have the signature for result_of contain references:
The result_of specification says the parameter types in the signature type should be interpreted as types of L-Value arguments used in a call expression (cited from memory).
Hmm - yes, the proposal says: "given types F, T1, T2, ..., TN and lvalues f, t1, t2, ..., tN of those types, respectively, the type expression result_of<F(T1, T2, ..., TN)>::type evaluates to the type of the expression f(t1, t2, ..., tN)." and then the following example depicts an operator() that takes references (and the references are missing from the result<> specification. Of course, this makes perfect sense, and of course, I missed it as well... :-) Yikes! [goes off to change a bunch of code] Thanks, Stjepan

Stjepan Rajko wrote:
On 7/12/07, Tobias Schwinger <tschwinger@isonews2.com> wrote:
It's wrong to have the signature for result_of contain references:
The result_of specification says the parameter types in the signature type should be interpreted as types of L-Value arguments used in a call expression (cited from memory).
Hmm - yes, the proposal says:
"given types F, T1, T2, ..., TN and lvalues f, t1, t2, ..., tN of those types, respectively, the type expression
result_of<F(T1, T2, ..., TN)>::type
evaluates to the type of the expression f(t1, t2, ..., tN)."
This was once true but is no longer. The final version of the result_of wording says that t1 is an lvalue when T1 is a reference, an rvalue otherwise.

On 7/12/07, Peter Dimov <pdimov@pdimov.com> wrote:
Stjepan Rajko wrote:
On 7/12/07, Tobias Schwinger <tschwinger@isonews2.com> wrote:
It's wrong to have the signature for result_of contain references:
The result_of specification says the parameter types in the signature type should be interpreted as types of L-Value arguments used in a call expression (cited from memory).
Hmm - yes, the proposal says:
"given types F, T1, T2, ..., TN and lvalues f, t1, t2, ..., tN of those types, respectively, the type expression
result_of<F(T1, T2, ..., TN)>::type
evaluates to the type of the expression f(t1, t2, ..., tN)."
This was once true but is no longer. The final version of the result_of wording says that t1 is an lvalue when T1 is a reference, an rvalue otherwise.
[comes to a screeching halt from changing code] I'm still not entirely on very sure footing when it comes to rvalues and lvalues, so can I bother you with a practical clarification? So, if I have, say, a function object with operator()(int &), I should specify result<F(int &)>, (but maybe if I have operator()(const int &), or operator()(int), I can specify result<F(int)>?) Sorry if these are silly questions, like I said I'm not 100% sure on what exactly this wording would mean, and I want to make sure I am doing the right thing. Thanks in advance, Stjepan

Stjepan Rajko wrote:
[comes to a screeching halt from changing code]
Yep. :-)
I'm still not entirely on very sure footing when it comes to rvalues and lvalues, so can I bother you with a practical clarification?
So, if I have, say, a function object with operator()(int &), I should specify result<F(int &)>, (but maybe if I have operator()(const int &), or operator()(int), I can specify result<F(int)>?)
If you have operator()( int& ), you need result<F(int&)>. If you have operator()( int ), you need both result<F(int)> and result<F(int&)> because your operator() can take either an lvalue or an rvalue. In this simple case you can just provide result_type, of course. You are allowed to provide result<F(int)> in addition to result<F(int&)> even in the first case; failing invalid queries is not required. What this means in practice is that in 99% of the cases you just need to strip the reference from the argument types when providing result or a result_of specialization. Only odd function objects such as struct F { int operator()( int& ); void operator()( long ); }; need special handling.

On 7/12/07, Peter Dimov <pdimov@pdimov.com> wrote:
Stjepan Rajko wrote:
So, if I have, say, a function object with operator()(int &), I should specify result<F(int &)>, (but maybe if I have operator()(const int &), or operator()(int), I can specify result<F(int)>?)
If you have operator()( int& ), you need result<F(int&)>. If you have operator()( int ), you need both result<F(int)> and result<F(int&)> because your operator() can take either an lvalue or an rvalue.
In this simple case you can just provide result_type, of course.
You are allowed to provide result<F(int)> in addition to result<F(int&)> even in the first case; failing invalid queries is not required.
What this means in practice is that in 99% of the cases you just need to strip the reference from the argument types when providing result or a result_of specialization. Only odd function objects such as
struct F { int operator()( int& ); void operator()( long ); };
need special handling.
Thank you for the detailed explanation! It is much clearer now. Stjepan

Peter Dimov wrote:
Stjepan Rajko wrote:
[comes to a screeching halt from changing code]
Yep. :-)
Sorry for the false alarm!
I'm still not entirely on very sure footing when it comes to rvalues and lvalues, so can I bother you with a practical clarification?
So, if I have, say, a function object with operator()(int &), I should specify result<F(int &)>, (but maybe if I have operator()(const int &), or operator()(int), I can specify result<F(int)>?)
If you have operator()( int& ), you need result<F(int&)>. If you have operator()( int ), you need both result<F(int)> and result<F(int&)> because your operator() can take either an lvalue or an rvalue.
We'd also need 'int const &' (given we don't just provide 'result_type' or deduce whatever it is), right? Regards, Tobias

Tobias Schwinger wrote:
Peter Dimov wrote:
Stjepan Rajko wrote:
[comes to a screeching halt from changing code]
Yep. :-)
Sorry for the false alarm!
I'm still not entirely on very sure footing when it comes to rvalues and lvalues, so can I bother you with a practical clarification?
So, if I have, say, a function object with operator()(int &), I should specify result<F(int &)>, (but maybe if I have operator()(const int &), or operator()(int), I can specify result<F(int)>?)
If you have operator()( int& ), you need result<F(int&)>. If you have operator()( int ), you need both result<F(int)> and result<F(int&)> because your operator() can take either an lvalue or an rvalue.
We'd also need 'int const &' (given we don't just provide 'result_type' or deduce whatever it is), right?
And bool, and char, and double, and so on; every argument type that can be passed to operator(). It's not practical to enumerate everything. But to answer your question, yes, a const lvalue of type int is a valid argument and needs to be supported by your specialization/result.

Peter Dimov wrote:
Stjepan Rajko wrote:
On 7/12/07, Tobias Schwinger <tschwinger@isonews2.com> wrote:
It's wrong to have the signature for result_of contain references:
The result_of specification says the parameter types in the signature type should be interpreted as types of L-Value arguments used in a call expression (cited from memory).
Hmm - yes, the proposal says:
"given types F, T1, T2, ..., TN and lvalues f, t1, t2, ..., tN of those types, respectively, the type expression
result_of<F(T1, T2, ..., TN)>::type
evaluates to the type of the expression f(t1, t2, ..., tN)."
This was once true but is no longer. The final version of the result_of wording says that t1 is an lvalue when T1 is a reference, an rvalue otherwise.
Seems I correctly remembered the wrong version :-/. Thanks for this catch (or maybe it was a throw?) We should check that Fusion properly adds references, as the previous result computation protocol used const/non-const L-Value types without reference. Looking into the Boost.ResultOf docs reveals that all three versions of this text are outdated. Seems a good idea to fix it, too. Regards, Tobias
participants (4)
-
Peter Dimov
-
shunsuke
-
Stjepan Rajko
-
Tobias Schwinger