Checking for return types (result_of) - redux..

Dear all, So, I have nearly figured out what to do, however it seems -- deliberately in fact, that the behaviour of result_of<???> is different when passed a function and a functor. Though I am not sure, the documentation: http://www.boost.org/doc/libs/1_39_0/libs/utility/ utility.htm#result_of "When F is a class type with a **member type result_type**, result_of<F(T1, T2, ..., TN)> is F::result_type." appears to imply that this is the way it should be (emphasis mine). So does the following in fact, http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1454.html So it seems that my original instinct (it's hard) was correct after all. Surely result_of<functor()>::type should resolve to the result type in the same way as it does for anything else callable (like a function). Anything else seems to be a big fat pain in the rear... Or, put another way, can I get what I want? Example below, the problem is s.member_function(one). Help! -ed #include <boost/mpl/assert.hpp> #include <boost/utility/result_of.hpp> #include <boost/type_traits/is_same.hpp> void void_f() {} unsigned unsigned_f() { return 1; } // We want member_function to accept any callable object (functor, // function pointer) thing that returns an unsigned integer. // // value = f(); // // Should work. struct S { template <class O> void member_function(O f) { // We require that the function/functor f returns an unsigned integer. BOOST_MPL_ASSERT( (boost::is_same<unsigned, typename boost::result_of<O()>::type>) ); unsigned sum(0); sum += f(); } }; struct one_functor { // This is apparently needed if we want to use result_of<..> on nullary functors like this. // typedef unsigned result_type; unsigned operator()() { return 1; } }; int main() { S s; one_functor one; unsigned val=one(); // We can assign to the val from the // instantiated functor. // THIS DOES NOT WORK AS IT SHOULD... s.member_function(one); // This does not work as // boost::result_of<O()> is, apparently // one_functor instead of unsigned // // Uncommenting the typedef in one_functor makes it // work - but it seems like a right pain in the rear. s.member_function(unsigned_f); // Works fine. // s.member_function(void_f); // Dies as it should! return 0; }

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Friday 21 August 2009, Edward Grace wrote:
boost::result_of<O()>::type
I don't have experience with boost::result_of, but is it right to add parenthesis? That looks to me like you are asking for the return type of a function type that takes no arguments and returns an object of type O. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkqOqiMACgkQ5vihyNWuA4VmAQCfQ3vE/kErgej1+hme4nKc2W1a IdkAoIM5DsU0x952LBmr/HX4sSES1BnS =NNSp -----END PGP SIGNATURE-----

Frank Mori Hess wrote:
Exactly. You beat me to it. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

AMDG Frank Mori Hess wrote:
This use of result_of is correct. result_of is designed to handle polymorphic function objects which can have a different return type depending on the arguments. boost::result_of<F()>::type means the result of calling an object of type F with no arguments. boost::result_of<F(int)> means the result of calling an object of type F with an int argument. In Christ, Steven Watanabe

On 21 Aug 2009, at 15:07, Frank Mori Hess wrote:
Me neither... Well, if you don't count the last couple of hours.
but is it right to add parenthesis?
I don't think so, from the doc: "Given an lvalue f of type F and lvalues t1, t2, ..., tN of types T1, T2, ..., TN, respectively, the type result_of<F(T1, T2, ..., TN)
::type defines the result type of the expression f(t1, t2, ...,tN). "
Since my function/functors take no arguments I took that to mean, assuming a function 'o' corresponding to the funtion type O result_of<O()>::type should resolve to the result (return type) of doing 'o()' - the function o(). This does appear to be the case in the code I submitted, at least for the ordinary functions. If I omit the parentheses then it compares the type of O (a function pointer returning an unsigned int) and unsigned int -- not what I want. Perhaps 'result type' and 'return type' are two subtly different things in this context.
That looks to me like you are asking for the return type of a function type that takes no arguments and returns an object of type O.
I don't think so - but I'm a little stumped... -ed

AMDG Edward Grace wrote:
The type of a function pointer is unsigned(*)(). The return type is easily deducible from the type of the function pointer. For function objects, you need to use result_type or result, since it is impossible to deduce the result type automatically
In the current standard it is impossible to deduce the result without using the result_type typedef. In C++0x, result_of is specified using decltype, so it won't be necessary. In Christ, Steven Watanabe

Hi Steven, Thanks for your response, I think it's going to save me a lot of messing around! On 21 Aug 2009, at 15:29, Steven Watanabe wrote:
Ok, that's kind of what I suspected -- but I didn't know why.
At least this means I won't waste my time trying to figure out what I'm doing wrong! It seems quite unedifying having to make sure the user adds some, apparently arbitrary, magic typedef to their functors in order to make sure it works. My original motivation was to form a helpful compile time assert for the user, which I thought would be good practise. It appears all this does is to shift a conceptually straightforward problem (make sure your functor/function returns something) to an even more obscure one "ordinary functions are fine but make sure you define a return_type typedef in your functions oh.. and make sure the return type of the operator()() is the same as that... " Then I have to add a way of sanely catching if they have not done this - I doubt I can... Crazy! Do you know if, boost::bind and or boost::function etc automatically add this typedef? If so some of the pain (and errors) would be alleviated by the automatic typedef. I they don't, perhaps they should. To make matters worse (rhetorical); if I have a class with a set of different functors each with a different return type how can I make sure the magic typedef (result_type) is simultaneously correct for each? Yuck! So, my best solution -- I think -- is to make sure my functions/ functors are unary and of the form: void function_body(double return&); that should be fairly easy to check for in all cases. To users it will appears weird in the extreme of course..... ...but it's C++, they should be used to it by now (ducks and exits stage left).
In C++0x, result_of is specified using decltype, so it won't be necessary.
Rapidly I'm coming to the conclusion that I'm not going to bother writing templated code until C++0x is finished -- there's (still) too much missing. Boost does a good job of papering over many of the cracks, but they are still there under the surface! Cheers, -ed

Edward Grace wrote:
Why not define your code to take a boost::function<double(/* whatever goes here */)>? Is the argument list variable? If so, aren't you already aware of that variance to use in the function type argument for boost::function? If that doesn't work, you'll need all of the crazy stuff that template argument packs will solve conveniently to accept N arguments: template <class R> R foo(boost::function<R()>); template <class R, class Arg1> R foo(boost::function<R(Arg1)>); template <class R, class Arg1, class Arg2> R foo(boost::function<R(Arg1, Arg2)>); etc. HTH, _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Hi Robert,
That well be the way to go. It's worth a shot... I was (still am slightly) reluctant to do this for the following reasons. a) I think minimising interdependencies (#includ ing stuff) is a good idea, of course that grand idea got drop kicked out of the window ages ago. I'm #include <boost/....>'d to the eyeballs now, one more won't hurt! b) It's for a generic function timer code, for measuring the relative performance of functions. Therefore I wanted to try and keep any cruft surrounding the callee as minimal as possible -- at least until I can be reasonably sure it has no impact. I'll give it a bash and see, Cheers, -ed ------------------------------------------------ "No more boom and bust." -- Dr. J. G. Brown, 1997

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Friday 21 August 2009, Edward Grace wrote:
It's not quite that bad. A result_type typedef is pretty standard for functors, for example std::unary_function and std::binary_function provide it. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkqOugsACgkQ5vihyNWuA4VLVQCcCk0rIHGPaZD1pYW04du2Y7WV 34UAn3y7H5NWz/kkn7O62WNmMCz5ABx2 =4xSO -----END PGP SIGNATURE-----

AMDG Edward Grace wrote:
Yes, they do.
In this case, you would need to use the more complex result template. struct square { template<class Signature> struct result; template<class This, class T> struct result<This(T)> { typedef typename boost::remove_const< typename boost::remove_reference<T>::type >::type type; }; template<class T> T operator()(const T& t) const { return t * t; } };
If all you are doing is checking the return type, you are probly better off using Boost.ConceptCheck. In Christ, Steven Watanabe

Ouch! Crikey Batman!
That does, conceptually, (pardon the pun) seem like a good idea. While my above kludge will work, along with the use of boost::function I'd rather get what I want -- a guarantee of the return type being correct or a compile error, than effect that outcome through some other means. I'm not (at all) familiar with it's usage of Boost.ConceptCheck, something like this perhaps? template <class O> BOOST_CONCEPT_REQUIRES( ((Generator)), (double) ) void class_scope<types...>::measure_execution_time(O f,.... etc...) { code goes here... } Here O is the abstract function object, it could be a member function ordinary function, anything that can be invoked with f(); Since it takes no arguments it's presumably a model of 'Generator' and I require it's return type to be 'double'. Before I get knee deep in compiler errors, does that seem about right to you? Cheers for this, -ed

Edward Grace wrote:
http://www.boost.org/doc/libs/1_39_0/libs/concept_check/reference.htm#functi... I use BOOST_CONCEPT_ASSERT in my own code: template< ... > template< class O > void class_scope< ... >::measure_execution_time(O f, ...) { // Assert that, e.g., // double x = f(); // is okay. BOOST_CONCEPT_ASSERT((boost::Generator< O, double >)); ... } I expect the following (or something like it) is what you'd do if you prefer BOOST_CONCEPT_REQUIRES, but as alluded to above, I've never actually used BOOST_CONCEPT_REQUIRES. template< ... > template< class O > BOOST_CONCEPT_REQUIRES( ((boost::Generator< O, double >)), void ) class_scope< ... > ::measure_execution_time(O f, ...) { ... } - Jeff
participants (5)
-
Edward Grace
-
Frank Mori Hess
-
Jeffrey Hellrung
-
Steven Watanabe
-
Stewart, Robert