
I'm sure this has been asked before, but searching the archives I cannot find anything. Is there some tutorial material on result_of anywhere? The boost documentation simply points to n1454, which doesn't contain much in the way of examples, and the TR1 document (n1647) which has no examples at all. Cheers, Ian

Ian McCulloch wrote:
I'm sure this has been asked before, but searching the archives I cannot find anything. Is there some tutorial material on result_of anywhere? The boost documentation simply points to n1454, which doesn't contain much in the way of examples, and the TR1 document (n1647) which has no examples at all.
Mini-result_of-tutorial: Q: What do I write in the space marked ??? below: template<class F, class A1, ..., class An> ??? apply( F f, A1 & a1, ..., An & an ) { return f( a1, ..., an ); } A: typename result_of<F(A1&, ..., An&)>::type.

Peter Dimov wrote:
Ian McCulloch wrote:
I'm sure this has been asked before, but searching the archives I cannot find anything. Is there some tutorial material on result_of anywhere? The boost documentation simply points to n1454, which doesn't contain much in the way of examples, and the TR1 document (n1647) which has no examples at all.
Mini-result_of-tutorial:
Q: What do I write in the space marked ??? below:
template<class F, class A1, ..., class An> ??? apply( F f, A1 & a1, ..., An & an ) { return f( a1, ..., an ); }
A:
typename result_of<F(A1&, ..., An&)>::type.
Is there any way to handle implicit conversions?

Ian McCulloch wrote:
Peter Dimov wrote:
Mini-result_of-tutorial:
Q: What do I write in the space marked ??? below:
template<class F, class A1, ..., class An> ??? apply( F f, A1 & a1, ..., An & an ) { return f( a1, ..., an ); }
A:
typename result_of<F(A1&, ..., An&)>::type.
Is there any way to handle implicit conversions?
No, if I understand your question correctly. That is, from the caller's point of view there is nothing to handle: you just pass the types of a1, ..., an to result_of. However writing the correct result_of specialization for a function object that is not templated on its argument types, but has two or more operator() overloads and relies on implicit conversions would be a major pain. Do you have a use case in mind?

Peter Dimov wrote:
Ian McCulloch wrote:
Peter Dimov wrote:
Mini-result_of-tutorial:
Q: What do I write in the space marked ??? below:
template<class F, class A1, ..., class An> ??? apply( F f, A1 & a1, ..., An & an ) { return f( a1, ..., an ); }
A:
typename result_of<F(A1&, ..., An&)>::type.
Is there any way to handle implicit conversions?
No, if I understand your question correctly. That is, from the caller's point of view there is nothing to handle: you just pass the types of a1, ..., an to result_of. However writing the correct result_of specialization for a function object that is not templated on its argument types, but has two or more operator() overloads and relies on implicit conversions would be a major pain.
Do you have a use case in mind?
Yes, if you want to forward to a function but you don't know if it takes arguments by value or const reference. I guess one could insist on using call_traits throughout, that is probably good enough for my application.

Ian McCulloch wrote:
Yes, if you want to forward to a function but you don't know if it takes arguments by value or const reference.
It doesn't matter. When you forward, you pass to result_of the exact types of the arguments you are supplying in the function (object) call, with a reference when the argument is an lvalue. int x; long y; int const z; int g(); int & h(); f(x, y); // result_of<F(int&, long&)> f(1, x); // result_of<F(int, int&)> f(z, 4); // result_of<F(int const&, int)> f( g(), h() ); // result_of<F(int, int&)> What is 'f' and how it takes its arguments in the cases above does not affect how result_of is used.

Peter Dimov wrote:
Ian McCulloch wrote:
Yes, if you want to forward to a function but you don't know if it takes arguments by value or const reference.
It doesn't matter. When you forward, you pass to result_of the exact types of the arguments you are supplying in the function (object) call, with a reference when the argument is an lvalue.
int x; long y; int const z; int g(); int & h();
f(x, y); // result_of<F(int&, long&)> f(1, x); // result_of<F(int, int&)> f(z, 4); // result_of<F(int const&, int)> f( g(), h() ); // result_of<F(int, int&)>
What is 'f' and how it takes its arguments in the cases above does not affect how result_of is used.
But what happens if F doesn't have an operator() overload that takes F(int const&, int) but only has F(int, int) ? It seems you need to know this in advance. I tried detecting it with SFINAE but that doesn't work; I would have expected that if result<F(...)> has no member type then result_of<F(...)> would simply be an empty struct, but instead the program is ill-formed. What I am trying to do is forward calls to an object with an overloaded operator() and nested struct result defined appropriately, but I don't know whether the operator() wants pass-by-value or pass-by-const-reference. Cheers, Ian

Ian McCulloch wrote:
Peter Dimov wrote:
Ian McCulloch wrote:
Yes, if you want to forward to a function but you don't know if it takes arguments by value or const reference.
It doesn't matter. When you forward, you pass to result_of the exact types of the arguments you are supplying in the function (object) call, with a reference when the argument is an lvalue.
int x; long y; int const z; int g(); int & h();
f(x, y); // result_of<F(int&, long&)> f(1, x); // result_of<F(int, int&)> f(z, 4); // result_of<F(int const&, int)> f( g(), h() ); // result_of<F(int, int&)>
What is 'f' and how it takes its arguments in the cases above does not affect how result_of is used.
But what happens if F doesn't have an operator() overload that takes F(int const&, int) but only has F(int, int) ? It seems you need to know this in advance.
No, you don't have to know this in advance. It is up to the implementation or specialization of result_of<F(...)> to handle these cases and return the correct result.

Peter Dimov wrote:
Ian McCulloch wrote:
Peter Dimov wrote:
Ian McCulloch wrote:
Yes, if you want to forward to a function but you don't know if it takes arguments by value or const reference.
It doesn't matter. When you forward, you pass to result_of the exact types of the arguments you are supplying in the function (object) call, with a reference when the argument is an lvalue.
int x; long y; int const z; int g(); int & h();
f(x, y); // result_of<F(int&, long&)> f(1, x); // result_of<F(int, int&)> f(z, 4); // result_of<F(int const&, int)> f( g(), h() ); // result_of<F(int, int&)>
What is 'f' and how it takes its arguments in the cases above does not affect how result_of is used.
But what happens if F doesn't have an operator() overload that takes F(int const&, int) but only has F(int, int) ? It seems you need to know this in advance.
No, you don't have to know this in advance. It is up to the implementation or specialization of result_of<F(...)> to handle these cases and return the correct result.
I'm not sure what you mean here. Do you mean that I should make sure that F::result<F(int, int>) and F::result<F(int const&, int)> are both defined? Otherwise I don't understand what magic incantions result_of would have to do to figure it out. In the following sample program, I have a functor that takes its argument by const reference, but the apply() function fails because it tries to pass by value: /usr/local/include/boost/utility/result_of.hpp: In instantiation of `boost::detail::get_result_of<negate_f, negate_f ()(int), false>': resultof_test.cpp:53: instantiated from `boost::detail::result_of<negate_f, negate_f ()(int)>' resultof_test.cpp:53: instantiated from `boost::result_of<negate_f ( (int)>' resultof_test.cpp:53: instantiated from here /usr/local/include/boost/utility/result_of.hpp:43: error: no type named `type' in `struct negate_f::result<negate_f ()(int)>' But if I change apply() to use pass by const-reference then it fails if I try to use it with a functor that uses pass by value. But maybe I am using result_of incorrectly. #include <boost/utility/result_of.hpp> // some example function that passes by const_reference template <typename T> struct negate { typedef T const& argument_type; typedef T result_type; result_type operator()(argument_type x) const { return -x; } }; // but we don't want to have to specify the parameter types by hand struct negate_f { template <typename T> struct result {}; template <typename T> struct result<negate_f(T const&)> // exact argument_type of negate<T> { typedef typename negate<T>::result_type type; }; template <typename T> typename result<negate_f(T const&)>::type operator()(T const& x) const { return negate<T>()(x); } }; // generic apply function // do we pass by value or const reference here? template <typename Func, typename T> typename boost::result_of<Func(T)>::type apply(Func f, T x) { return f(x); } int main() { apply(negate_f(), 5); } Cheers, Ian

Ian McCulloch wrote:
Peter Dimov wrote:
No, you don't have to know this in advance. It is up to the implementation or specialization of result_of<F(...)> to handle these cases and return the correct result.
I'm not sure what you mean here. Do you mean that I should make sure that F::result<F(int, int>) and F::result<F(int const&, int)> are both defined?
Yes, if you F can be called with an rvalue of type int and with an lvalue of type const int. [...]
#include <boost/utility/result_of.hpp>
// some example function that passes by const_reference template <typename T> struct negate { typedef T const& argument_type; typedef T result_type;
result_type operator()(argument_type x) const { return -x; } };
// but we don't want to have to specify the parameter types by hand struct negate_f { template <typename T> struct result {};
template <typename T> struct result<negate_f(T const&)> // exact argument_type of negate<T> { typedef typename negate<T>::result_type type; };
This specialization says that negate_f can only accept const lvalues.
template <typename T> typename result<negate_f(T const&)>::type operator()(T const& x) const { return negate<T>()(x); } };
// generic apply function // do we pass by value or const reference here? template <typename Func, typename T> typename boost::result_of<Func(T)>::type
This says that you ask for type of the return value of F when passed an rvalue of type T.
apply(Func f, T x) { return f(x);
But you pass an lvalue of type T here.
}
int main() { apply(negate_f(), 5); }
Try this variation instead: #include <boost/utility/result_of.hpp> #include <boost/type_traits/remove_const.hpp> #include <boost/type_traits/remove_reference.hpp> struct negate; namespace boost { template<class T> struct result_of<negate(T)> { typedef typename remove_reference<T>::type no_ref; typedef typename remove_const<no_ref>::type type; }; } // boost struct negate { template<class T> T operator()(T const & x) const { return -x; } }; template <typename Func, typename T> typename boost::result_of<Func(T&)>::type apply(Func f, T x) { return f(x); } int f( int x ) { return -x; } int main() { apply( f, 5 ); apply( negate(), 5 ); }

Hi everybody, I am rather curious about how static visitation on boost::variant works and I was wondering if someone can enlighten me about this. For example, consider the following: boost::variant<long, std::string> vFoo; class my_visitor : public boost::static_visitor<> { operator()(long nValue) { ... } operator()(std::string strValue) { ... } }; vFoo = 1; //"current" type is long boost::apply_visitor(my_visitor(), vFoo); //call operator()(long nValue) vFoo = "me"; //"current" type is std::string boost::apply_visitor(my_visitor(), vFoo); //call operator()(std::string strValue) What I do not understand is, the "current" variant type seems to be changed at runtime (at assignment), then how can apply_vistor know which operator() to call at compile time? Thanks -delfin

What I do not understand is, the "current" variant type seems to be changed at runtime (at assignment), then how can apply_vistor know which operator() to call at compile time?
switch by type index and static cast to appropriate type. Gennadiy

I see, so at compile time there is a "switch" built that will call the appropriate visitor method. Then at run time the "current" type index is run through this "switch". Makes sense, Thanks -delfin
-----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users- bounces@lists.boost.org] On Behalf Of Gennadiy Rozental Sent: Wednesday, March 16, 2005 1:02 PM To: boost-users@lists.boost.org Subject: [Boost-users] Re: How does boost::variant static visitation work?
What I do not understand is, the "current" variant type seems to be changed at runtime (at assignment), then how can apply_vistor know which operator() to call at compile time?
switch by type index and static cast to appropriate type.
Gennadiy
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

"Delfin Rojas" <drojas@moodlogic.com> writes:
Hi everybody,
I am rather curious about how static visitation on boost::variant works and I was wondering if someone can enlighten me about this. For example,
<snip> Please don't start new subjects by replying to existing messages. It messes up threading. http://www.boost.org/more/discussion_policy.htm#quoting -- Dave Abrahams Boost Consulting www.boost-consulting.com

I'll keep in mind, sorry about that. -delfin
-----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users- bounces@lists.boost.org] On Behalf Of David Abrahams Sent: Thursday, March 17, 2005 7:03 AM To: boost-users@lists.boost.org Subject: [Boost-users] Starting New Threads (was: How does boost::variantstatic visitation work?)
"Delfin Rojas" <drojas@moodlogic.com> writes:
Hi everybody,
I am rather curious about how static visitation on boost::variant works and I was wondering if someone can enlighten me about this. For example,
<snip>
Please don't start new subjects by replying to existing messages. It messes up threading.
http://www.boost.org/more/discussion_policy.htm#quoting
-- Dave Abrahams Boost Consulting www.boost-consulting.com
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

Thanks Peter, I think I understand how it works now. Unfortunately, the lack of SFINAE is a real showstopper for me. I want to write functions like template <typename T> typename result_of<negate(T)>::type operator-(T const& x) { return negate()(x); } but the lack of SFINAE here makes boost::result_of essentially useless for this. But I imagine it won't be difficult to make a new version based on boost that would work. Cheers, Ian

Ian McCulloch wrote:
Thanks Peter, I think I understand how it works now. Unfortunately, the lack of SFINAE is a real showstopper for me. I want to write functions like
template <typename T> typename result_of<negate(T)>::type operator-(T const& x) { return negate()(x); }
but the lack of SFINAE here makes boost::result_of essentially useless for this.
Yes, I've encountered this too. The problem is in the unconditional instantiation of the nested ::result<>. Since we don't have a reliable way to detect a nested template yet (requires core language change), this can't be fixed except by removing the ::result<> logic altogether and relying on specializations instead, as in the example I gave. This won't help us in general, though. result_of is not supposed to support SFINAE, because it may be implemented with __typeof or __decltype internally.

Peter Dimov wrote:
Ian McCulloch wrote:
Thanks Peter, I think I understand how it works now. Unfortunately, the lack of SFINAE is a real showstopper for me. I want to write functions like
template <typename T> typename result_of<negate(T)>::type operator-(T const& x) { return negate()(x); }
but the lack of SFINAE here makes boost::result_of essentially useless for this.
Yes, I've encountered this too. The problem is in the unconditional instantiation of the nested ::result<>. Since we don't have a reliable way to detect a nested template yet (requires core language change), this can't be fixed except by removing the ::result<> logic altogether and relying on specializations instead, as in the example I gave.
I don't get it: can't you detect whether nested::result<nested(T)>::type exists? As long as the primary template of nested::result is defined as an empty struct this should work fine(?).
This won't help us in general, though. result_of is not supposed to support SFINAE, because it may be implemented with __typeof or __decltype internally.
Do you mean that a substitution failure in a typeof or decltype expression *is* an error? Ouch! Cheers, Ian

Ian McCulloch wrote:
Peter Dimov wrote:
[...]
I don't get it: can't you detect whether nested::result<nested(T)>::type exists? As long as the primary template of nested::result is defined as an empty struct this should work fine(?).
No, consider what happens when 'nested' is int (*) (), for example.
This won't help us in general, though. result_of is not supposed to support SFINAE, because it may be implemented with __typeof or __decltype internally.
Do you mean that a substitution failure in a typeof or decltype expression *is* an error? Ouch!
The current C++ compilers can't backtrack on an error, so once decltype(expr) triggers a complicated chain of events potentially involving multiple implicit template instantiations, there is no way back. Or so I hear. :-)

Peter Dimov wrote:
Ian McCulloch wrote:
Peter Dimov wrote:
[...]
I don't get it: can't you detect whether nested::result<nested(T)>::type exists? As long as the primary template of nested::result is defined as an empty struct this should work fine(?).
No, consider what happens when 'nested' is int (*) (), for example.
Hm, it can't be int (*) (), because result_of will not attempt to look for a nested result, but it can be an user-defined type with no nested result, or a member named result.

Peter Dimov wrote:
Peter Dimov wrote:
Ian McCulloch wrote:
Peter Dimov wrote:
[...]
I don't get it: can't you detect whether nested::result<nested(T)>::type exists? As long as the primary template of nested::result is defined as an empty struct this should work fine(?).
No, consider what happens when 'nested' is int (*) (), for example.
Hm, it can't be int (*) (), because result_of will not attempt to look for a nested result, but it can be an user-defined type with no nested result, or a member named result.
Wouldn't both those cases (no nested result, or a member named result) cause a substitution failure? #include <iostream> struct foo { template <typename T> struct result { typedef int type; }; }; struct bar { template <typename T> struct result {}; }; struct baz { void result() {} }; struct bat { }; void func(...) { std::cout << "func(...)\n"; } template <typename T> typename T::result<T(int)>::type func(T const& x) { std::cout << "func(T const&)\n"; return typename T::result<T(int)>::type(); } int main() { func(foo()); func(bar()); func(baz()); func(bat()); } This compiles cleanly with g++ 3.3.4 and produces func(T const&) func(...) func(...) func(...) Cheers, Ian

Ian McCulloch wrote:
Peter Dimov wrote:
Peter Dimov wrote:
Ian McCulloch wrote:
Peter Dimov wrote:
[...]
I don't get it: can't you detect whether nested::result<nested(T)>::type exists? As long as the primary template of nested::result is defined as an empty struct this should work fine(?).
No, consider what happens when 'nested' is int (*) (), for example.
Hm, it can't be int (*) (), because result_of will not attempt to look for a nested result, but it can be an user-defined type with no nested result, or a member named result.
Wouldn't both those cases (no nested result, or a member named result) cause a substitution failure?
Allowed but not required by 14.8.2/2, I think.
template <typename T> typename T::result<T(int)>::type func(T const& x) { std::cout << "func(T const&)\n"; return typename T::result<T(int)>::type(); }
There should be a "template" before "result".
This compiles cleanly with g++ 3.3.4
Yes, it also works on MSVC 7.1 and (with "template" added before "result") on Comeau 4.3.0.1 strict, so it seems that this is a "supported" case of substitution failure. However tr1::result_of implementations aren't required to use this trick (which, to the best of my knowledge, hasn't been publicized anywhere so far) to not generate errors when ::result<F(Ai...)>::type is not defined, and boost::result_of doesn't, either.

"Ian McCulloch" <ianmcc@physik.rwth-aachen.de> wrote
lack of SFINAE is a real showstopper for me. I want to write functions like
template <typename T> typename result_of<negate(T)>::type operator-(T const& x) { return negate()(x); }
but the lack of SFINAE here makes boost::result_of essentially useless for this. But I imagine it won't be difficult to make a new version based on boost that would work.
FWIW: template<class T> T make(); template <class T> BOOST_TYPEOF_TPL(make<std::negate<T> >()(T())) operator-(T const& x) { return std::negate<T>()(x); } Regards, Arkadiy

Arkadiy Vertleyb wrote:
"Ian McCulloch" <ianmcc@physik.rwth-aachen.de> wrote
lack of SFINAE is a real showstopper for me. I want to write functions like
template <typename T> typename result_of<negate(T)>::type operator-(T const& x) { return negate()(x); }
but the lack of SFINAE here makes boost::result_of essentially useless for this. But I imagine it won't be difficult to make a new version based on boost that would work.
FWIW:
template<class T> T make();
template <class T> BOOST_TYPEOF_TPL(make<std::negate<T> >()(T())) operator-(T const& x) { return std::negate<T>()(x); }
This looks very attractive, and I'll probably und up using it somewhere; but in the particular application I'm working on, something like result_of (but allows SFINAE) will do fine. I guess its just a matter of what changes I'll need to make when we get decltype. With the result_of approach I could get rid of the nested result<> structs and/or result_of specializations, with the BOOST_TYPEOF approach I could simplify the forwarding function declarations. I'm not sure what is the better tradeoff. Cheers, Ian

"Ian McCulloch" <ianmcc@physik.rwth-aachen.de> wrote
Arkadiy Vertleyb wrote:
template<class T> T make();
template <class T> BOOST_TYPEOF_TPL(make<std::negate<T> >()(T())) operator-(T const& x) { return std::negate<T>()(x); }
This looks very attractive, and I'll probably und up using it somewhere; but in the particular application I'm working on, something like result_of (but allows SFINAE) will do fine. I guess its just a matter of what changes I'll need to make when we get decltype. With the result_of approach I could get rid of the nested result<> structs and/or result_of specializations, with the BOOST_TYPEOF approach I could simplify the forwarding function declarations. I'm not sure what is the better tradeoff.
We have BOOST_LVALUE_TYPEOF(), which attempts to mimique decltype. It's not *quite* decltype, however :-( Also, where built-in typeof exists, we forward to it, so you don't have to pay for all the emulation stuff. The main drawbacks are: 1) The need to register user-defined types/templates (although it's pretty simple); 2) Potential ODR non-conformance, because of automatic ID generation/use of unnamed namespace (although it doesn't seem to show up in any compilers we tested with, and we specifically try to cause it); 3) Slower compile times if complicated types are used; 4) Some (workaroundable) problems with standard iterator registration in Dinkumware STL (because of nested classes/non-deduced context) If you can live with the above four, I believe our typeof emulation is worth trying. (Please be awere, however, that the above code I provided may not compile in the current submission version, if you use VC71. It is subject to ETI bug workaround that I found trying to adapt your code, so thanks! It works with my local version that I plan to upload sometime before the review. If you do need it sooner, please let me know) Regards, Arkadiy

"Ian McCulloch" <ianmcc@physik.rwth-aachen.de> wrote
Peter Dimov wrote:
Ian McCulloch wrote:
Yes, if you want to forward to a function but you don't know if it takes arguments by value or const reference.
It doesn't matter. When you forward, you pass to result_of the exact types of the arguments you are supplying in the function (object) call, with a reference when the argument is an lvalue.
int x; long y; int const z; int g(); int & h();
f(x, y); // result_of<F(int&, long&)> f(1, x); // result_of<F(int, int&)> f(z, 4); // result_of<F(int const&, int)> f( g(), h() ); // result_of<F(int, int&)>
What is 'f' and how it takes its arguments in the cases above does not affect how result_of is used.
But what happens if F doesn't have an operator() overload that takes F(int const&, int) but only has F(int, int) ? It seems you need to know this in advance. I tried detecting it with SFINAE but that doesn't work; I would have expected that if result<F(...)> has no member type then result_of<F(...)> would simply be an empty struct, but instead the program is ill-formed.
What I am trying to do is forward calls to an object with an overloaded operator() and nested struct result defined appropriately, but I don't know whether the operator() wants pass-by-value or pass-by-const-reference.
Are you sure you want result_of<> rather than typeof()? Would you consider typeof emulation library, that is in the review queue right now? Regards, Arkadiy
participants (6)
-
Arkadiy Vertleyb
-
David Abrahams
-
Delfin Rojas
-
Gennadiy Rozental
-
Ian McCulloch
-
Peter Dimov