Boost logo

Boost :

Subject: [boost] A possible solution to the SFINAE/decltype puzzler
From: pfultz2 (pfultz2_at_[hidden])
Date: 2015-02-01 00:57:57


A couple years ago Eric Niebler posted a question on here about improving
error messages with SFINAE and trailing decltype:

http://boost.2283326.n4.nabble.com/C-11-decltype-SFINAE-puzzler-td4632622.html

The problem was getting a useful error message when writing something like
this:

struct h_f
{
    template<class T>
    auto operator()(T x) const -> decltype(*x) { return *x; }
};
constexpr static h_f h = {};

struct g_f
{
    template<class T>
    auto operator()(T x) const -> decltype(h(x)) { return h(x); }
};
constexpr static g_f g = {};

struct f_f
{
    template<class T>
    auto operator()(T x) const -> decltype(g(x)) { return g(x); };
};
constexpr static f_f f = {};

int main()
{
    f(0);
}

His solution at the time was to turn the substitution failures into errors.
Of
course, this has some problems as well. First, it removes the template
constraints from the functions. Secondly, it can produce more errors(rather
than one error with multiple "notes"). Ideally, it would be better to
transport to the top the substitution failures. Well, using template aliases
this can be done:

https://gist.github.com/pfultz2/3d66795ef18361699fbb

When I compile this code I get an error like this:

reveal1.cpp:104:5: error: no matching function for call to object of type
'const reveal_failure<f_f>'
    f(0);
    ^
reveal1.cpp:56:10: note: candidate template ignored: substitution failure
[with Ts = <int>, Id = identity]: indirection requires pointer operand
('int' invalid)
    auto operator()(Ts&&... xs) const ->
decltype(F()(std::forward<Ts>(xs)...))
         ^

Now, when there is multiple overloads(or multiple notes), this can be
extended
to handle this situation, as well. An overload for each note, can be added.
Plus, the backtrace can be "flattened" so that only the "leafs" are shown. I
do this here:

https://gist.github.com/pfultz2/ac74129e73393f798e41

So for the 'advance' function it shows an error like this:

reveal2.cpp:288:5: error: no matching function for call to object of type
'reveal<conditional&lt;advance_advanceable, advance_decrementable,
advance_incrementable> >'
    advance(f, 1);
    ^~~~~~~
reveal2.cpp:215:13: note: candidate template ignored: disabled by
'enable_if' [with Ts = <foo &, int>, Id = identity]
            REQUIRE_OF(models<Advanceable(Iterator, int)>());
            ^
reveal2.cpp:7:82: note: expanded from macro 'REQUIRE_OF'
#define REQUIRE_OF(...) template<class Id> using apply = typename
std::enable_if<(Id()(__VA_ARGS__)), int>::type
                                                                                
^
reveal2.cpp:234:13: note: candidate template ignored: disabled by
'enable_if' [with Ts = <foo &, int>, Id = identity]
            REQUIRE_OF(models<Decrementable(Iterator)>());
            ^
reveal2.cpp:7:82: note: expanded from macro 'REQUIRE_OF'
#define REQUIRE_OF(...) template<class Id> using apply = typename
std::enable_if<(Id()(__VA_ARGS__)), int>::type
                                                                                
^
reveal2.cpp:258:13: note: candidate template ignored: disabled by
'enable_if' [with Ts = <foo &, int>, Id = identity]
            REQUIRE_OF(models<Incrementable(Iterator)>());
            ^
reveal2.cpp:7:82: note: expanded from macro 'REQUIRE_OF'
#define REQUIRE_OF(...) template<class Id> using apply = typename
std::enable_if<(Id()(__VA_ARGS__)), int>::type
                                                                                
^
reveal2.cpp:168:10: note: candidate template ignored: substitution failure
[with Ts = <foo &, int>]: no matching function for call to object of type
'advance_advanceable'
    auto operator()(Ts&&... xs) -> decltype(F1()(std::forward<Ts>(xs)...))
         ^ ~~
reveal2.cpp:174:10: note: candidate template ignored: substitution failure
[with Ts = <foo &, int>, $1 = 0]: no matching function for call to object of
type
      'conditional<advance_decrementable, advance_incrementable>'
    auto operator()(Ts&&... xs) -> decltype(F2()(std::forward<Ts>(xs)...))
         ^

So there is one note for each overload of `Advanceable`, `Decrementable`,
and
`Incrementable`. There is two additional notes at the bottom, this comes
from
the original function(ideally it would need to be normalized). I go into
more
detail about how this works here:

http://pfultz2.com/blog/2015/01/31/improving-error-messages/

However, this is isn't quite perfect yet. First, it requires putting the
type
requirements into a nested failure struct. Secondly, not all the types are
reported back to clang. It doesn't show the user what type `Iterator` is.
Ideally, it would be nice if there was a way to push clang to report the
substitution failure at least one level deeper for an expression. This is
about as close as I've gotten.

Is there any feedback? Questions? Perhaps some improvements?

Thanks,
Paul Fultz II

--
View this message in context: http://boost.2283326.n4.nabble.com/A-possible-solution-to-the-SFINAE-decltype-puzzler-tp4671871.html
Sent from the Boost - Dev mailing list archive at Nabble.com.

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk