Opened 10 years ago

Last modified 10 years ago

#7343 new Feature Requests

Extend result_of to work with SFINAE

Reported by: Daniel Walker Owned by: Daniel Walker
Milestone: To Be Determined Component: result_of
Version: Boost 1.52.0 Severity: Problem
Keywords: result_of, decltype, SFINAE Cc:

Description

In response to a problem first identified by Joel de Guzman:

http://thread.gmane.org/gmane.comp.lib.boost.devel/233752/focus=233934

When BOOST_RESULT_OF_USE_DECLTYPE is defined, the attached patch makes the expression result_of<Fn( ArgTypes ...)>::type not well formed when the expression decltype(INVOKE(declval< Fn >(), declval< ArgTypes >()...)) is not well formed, but does not break SFINAE when used as the return type of a function overload.

Attachments (2)

djw_result_of_sfinae.patch (4.5 KB ) - added by Daniel Walker 10 years ago.
updated patch incorporating suggestions from Michel Morin
ean_result_of_sfinae.patch (2.4 KB ) - added by Eric Niebler 10 years ago.
simpler patch, tested with clang-trunk

Download all attachments as: .zip

Change History (28)

by Daniel Walker, 10 years ago

Attachment: djw_result_of_sfinae.patch added

updated patch incorporating suggestions from Michel Morin

by Eric Niebler, 10 years ago

Attachment: ean_result_of_sfinae.patch added

simpler patch, tested with clang-trunk

comment:1 by Eric Niebler, 10 years ago

Daniel, I've attached a much simpler, lighter-weight patch. However, both of our patches are incomplete because of how result_of is defined:

// Uses declval following N3225 20.7.7.6 when F is not a pointer.
template<typename F BOOST_PP_COMMA_IF(BOOST_PP_ITERATION())
         BOOST_PP_ENUM_PARAMS(BOOST_PP_ITERATION(),typename T)>
struct result_of<F(BOOST_PP_ENUM_PARAMS(BOOST_PP_ITERATION(),T))>
    : mpl::if_<
          is_member_function_pointer<F>
        , detail::tr1_result_of_impl<
            typename remove_cv<F>::type,
            typename remove_cv<F>::type(BOOST_PP_ENUM_PARAMS(BOOST_PP_ITERATION(),T)), false
          >
        , detail::cpp0x_result_of_impl<
              F(BOOST_PP_ENUM_PARAMS(BOOST_PP_ITERATION(),T))
          >
      >::type
{};

If F is a member function pointer, the first branch of the mpl::if_ gets taken, and no clever SFINAE guards detail::tr1_result_of_impl.

But to a first approximation, I think this is good enough for 1.52. I tested my patch with clang-trunk, but not the most recent stable release, and not with msvc-11, which I'm downloading now. If you have access to those and want to get a jump on things, that'd be great. I'd like to get this change in to trunk ASAP so we have time to merge to release for 1.52.

Thanks for your work on this.

comment:2 by Daniel Walker, 10 years ago

Thanks Eric. Your patch looks reasonable, and I like the simplifications. I'll test on clang 2.1 later today and assuming all's well I'll check your patch into trunk. I'd also like to see this in the 1.52 release. To my knowledge this is not a breaking change, i.e. enabling SFINAE doesn't invalidate any previously valid code, so we should be good to go.

comment:3 by Eric Niebler, 10 years ago

Sadly, my patch doesn't work with msvc-11, but yours does. Unfortunately, yours has heavier TMP overhead. The best solution, IMO, would be to use yours for msvc-11 and use mine in other cases, assuming clang 3.1 can handle it. Thoughts?

comment:4 by Eric Niebler, 10 years ago

For the record, here is the msvc bug that is blocking the simpler implementation from working:

https://connect.microsoft.com/VisualStudio/feedback/details/763618/too-eager-instantiation-in-class-template-partial-specialization

comment:5 by Michel Morin, 10 years ago

I tested both versions (in a C++11 mode with BOOST_RESULT_OF_USE_DECLTYPE) and got the same results:

  • gcc 4.3-4.4: compilation error on sfinae_test
  • gcc 4.5-4.7, 4.8 (experimental): OK
  • clang 2.8-3.1, 3.2 (trunk): OK

I think gcc 4.3-4.4 had incomplete support of "SFINAE for expressions" (though BOOST_NO_SFINAE_EXPR is not defined on gcc 4.4).

comment:6 by Daniel Walker, 10 years ago

I combined the two patches so that the thin implementation is the default and the is_callable implementation is a workaround for MSVC tested at 1700. I tested on clang 3.2 and gcc 4.7 with BOOST_RESULT_OF_USE_DECLTYPE and can corroborate Michel's results. Eric could you test on MSVC one more time? If everything is OK, I think its ready to merge to release.

comment:7 by Michel Morin, 10 years ago

I tested r80605 (in a C++11 mode with BOOST_RESULT_OF_USE_DECLTYPE). The test results are the same as above:

  • gcc 4.3-4.4: compilation error on sfinae_test
  • gcc 4.5-4.7, 4.8 (experimental): OK
  • clang 2.8-3.1, 3.2 (trunk): OK

comment:8 by Eric Niebler, 10 years ago

Tests pass on msvc 8-11. That does *not* mean we're ready to merge to release, though. It means we're ready to wait and see what crops up in the trunk regression reports. :-)

comment:9 by Michel Morin, 10 years ago

I can see a tab in line 124 of result_of_iterate.hpp ;-)

comment:10 by Eric Niebler, 10 years ago

Bah. My bad. Fixed in [80608].

comment:11 by Michel Morin, 10 years ago

Eric, did you test on msvc 10 with BOOST_RESULT_OF_USE_DECLTYPE? (In other word, does the "Too-eager instantiation in class template partial specialization" bug happen only on msvc 11?)

comment:12 by Eric Niebler, 10 years ago

Good question. No, it doesn't compile on msvc 10 in that configuration. I'll look into it.

Regardless, I took the liberty of merging all the outstanding result_of changes to release, since the trunk tests looked no worse than before. When I get a handle on the msvc regression, I'll merge that too. For reference, the release changeset is [80621].

comment:13 by Eric Niebler, 10 years ago

Changeset [80636] on trunk changes the workaround for msvc and extends it to work for all compilers that don't do extended-sfinae-for-expressions. All the tests should pass now, even on gcc 4.3-4.4. Michel, can you give it a shot?

comment:14 by Daniel Walker, 10 years ago

Works on gcc 4.4. Nice! However, BOOST_NO_SFINAE_EXPR does not appear to be defined for 4.4. boost/config/gcc.hpp has

#if GNUC < 4
(GNUC == 4 && GNUC_MINOR < 4)

# define BOOST_NO_SFINAE_EXPR #endif

Should that be GNUC_MINOR < 5?

comment:15 by Eric Niebler, 10 years ago

I don't know. What's your reason for thinking that BOOST_NO_SFINAE_EXPR should be defined for gcc 4.4?

comment:16 by Daniel Walker, 10 years ago

I don't know either, but you ifdefed the workaround with BOOST_NO_SFINAE_EXPR. I had to define it in order to test your changes, which do work on gcc 4.4. The tests fail on gcc 4.4 without the workaround.

comment:17 by Michel Morin, 10 years ago

First, I did the following

  1. Remove BOOST_RESULT_OF_USE_DECLTYPE guard for sfinae_test in result_of_test.cpp.
  2. Make gcc 4.4 in a C++11 mode (with BOOST_RESULT_OF_USE_DECLTYPE) follow the BOOST_NO_SFINAE_EXPR branch.

Without applying 2, the test fails to compile on gcc 4.4 (in a C++11 mode BOOST_RESULT_OF_USE_DECLTYPE).

Here are the test results.

  • gcc 4.3 - 4.8 (in a C++03 mode): OK
  • gcc 4.3 - 4.8 (in a C++11 mode): OK
  • gcc 4.3 - 4.4 (in a C++11 mode + BOOST_RESULT_OF_USE_DECLTYPE): OK but with two warnings:
    boost/utility/detail/result_of_iterate.hpp:100: 
    warning: object of type 'volatile int&' will not be accessed 
    in left-hand operand of comma
    
    boost/utility/detail/result_of_iterate.hpp:100: 
    warning: object of type 'const volatile int&' will not be accessed 
    in left-hand operand of comma
    
  • gcc 4.5 - 4.8 (in a C++11 mode + BOOST_RESULT_OF_USE_DECLTYPE): OK
  • clang 2.8 - 3.2 (in a C++03 mode): OK
  • clang 2.8 - 3.2 (in a C++11 mode): OK
  • clang 2.8 - 3.2 (in a C++11 mode + BOOST_RESULT_OF_USE_DECLTYPE): OK

comment:18 by Michel Morin, 10 years ago

Full warning messages on gcc 4.3-4.4 (in a C++11 mode BOOST_RESULT_OF_USE_DECLTYPE):

boost/utility/detail/result_of_iterate.hpp: In instantiation of 'const bool boost::detail::result_of_is_callable_2<result_of_member_function_template, volatile int&, int>::value':
boost/utility/detail/result_of_iterate.hpp:101:   instantiated from 'boost::detail::result_of_is_callable_2<result_of_member_function_template, volatile int&, int>'
boost/utility/enable_if.hpp:47:   instantiated from 'boost::lazy_enable_if<boost::detail::result_of_is_callable_2<result_of_member_function_template, volatile int&, int>, boost::detail::cpp0x_result_of_impl<result_of_member_function_template ()(volatile int&, int), false> >'
boost/utility/result_of.hpp:118:   instantiated from 'boost::detail::cpp0x_result_of_impl<result_of_member_function_template ()(volatile int&, int), true>'
boost/utility/result_of.hpp:59:   instantiated from 'boost::result_of<result_of_member_function_template ()(volatile int&, int)>'
untitled:245:   instantiated from here
boost/utility/detail/result_of_iterate.hpp:100: warning: object of type 'volatile int&' will not be accessed in left-hand operand of comma
boost/utility/detail/result_of_iterate.hpp: In instantiation of 'const bool boost::detail::result_of_is_callable_2<result_of_member_function_template, const volatile int&, int>::value':
boost/utility/detail/result_of_iterate.hpp:101:   instantiated from 'boost::detail::result_of_is_callable_2<result_of_member_function_template, const volatile int&, int>'
boost/utility/enable_if.hpp:47:   instantiated from 'boost::lazy_enable_if<boost::detail::result_of_is_callable_2<result_of_member_function_template, const volatile int&, int>, boost::detail::cpp0x_result_of_impl<result_of_member_function_template ()(const volatile int&, int), false> >'
boost/utility/result_of.hpp:118:   instantiated from 'boost::detail::cpp0x_result_of_impl<result_of_member_function_template ()(const volatile int&, int), true>'
boost/utility/result_of.hpp:59:   instantiated from 'boost::result_of<result_of_member_function_template ()(const volatile int&, int)>'
untitled:246:   instantiated from here
boost/utility/detail/result_of_iterate.hpp:100: warning: object of type 'const volatile int&' will not be accessed in left-hand operand of comma

comment:19 by Michel Morin, 10 years ago

In the above warnings, untitled:*** means result_of_test.cpp:***. Sorry about that.

comment:20 by Eric Niebler, 10 years ago

Changeset [80654] applies the workaround to gcc-4.4 also. Can you give it a shot? And how do I suppress errors on gcc?

EDIT: How do I suppress *WARNINGS* on gcc?

Last edited 10 years ago by Eric Niebler (previous) (diff)

comment:21 by Michel Morin, 10 years ago

gcc has

#pragma GCC diagnostic ignored "-Wxxxx"

but, before gcc 4.6, the following is not supported.

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wxxxx"
#pragma GCC diagnostic pop

So we cannot restore the user's warning conditions.

Another way to disable warnings is add

#pragma GCC system_header

Adding this pragma into result_of.hpp or result_of_iterate.hpp removes the warning.

comment:22 by Daniel Walker, 10 years ago

Warnings suppressed in changeset [80655] on gcc. Thanks for all the work Eric. The users will appreciate it! And thanks for the help Michel.

comment:23 by Michel Morin, 10 years ago

I tested r80655 on gcc and clang and it works fine.

Daniel, I think

#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 5) && defined(BOOST_RESULT_OF_USE_DECLTYPE)
#pragma GCC system_header 
#endif

would be safer for the future development.

comment:24 by Eric Niebler, 10 years ago

I found a workaround for the gcc warnings in code. Changeset [80656] makes the change and removes the #pragma GCC system_header.

comment:25 by Michel Morin, 10 years ago

r80656 works fine on gcc and clang. Thanks Eric and Daniel for all the work!

comment:26 by Michel Morin, 10 years ago

Component: utilityresult_of
Note: See TracTickets for help on using tickets.