C++11 decltype/SFINAE puzzler

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
29 messages Options
12
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

C++11 decltype/SFINAE puzzler

Eric Niebler-3
I'm running into a usability problem with the extended SFINAE rules that
could effect best practice for all new C++ libraries. I'm hoping some
clever person in the Boost community can help me.

I find that in highly generic code, using decltype to define the return
type of functions is very handy. Dave A. suggested a RETURNS macro to
simplify this idiom:

<https://groups.google.com/d/msg/boost-devel-archive/OzJ5Ft3pSnU/b_Ter9bgNqAJ>

It's used like this:

  template<typename T>
  auto foo( T && t ) RETURNS( some-expression-using-t );

This is great and I use it a lot. Now, imagine you have a library of
such functions calling other such functions: function A calls B calls
... calls Y, calls Z, all of which use decltype to declare their return
types. Now, the user calls A passing a variable t for which the
expression in Z's return type calculation makes no sense. Extended
SFINAE kicks in, and Z simply disappears. Since Z has disappeared, the
expression in Y's return type makes no sense, so Y disappears, too. And
so on up the chain.

The end result is that the user is presented with an error like: "no
viable function A, template substitution failed." The user is given no
information about which function in the chain failed to compile, or why.
This is a serious usability problem!

So, my question is: what can be done about this? Can we get the benefit
of automatic type deduction in return types without causing SFINAE to
drop functions from the overload set? Is there another trick we can use
to report errors meaningfully when APIs are misused?

The only solution I can think of is turn all the free functions in the
chain into function objects and then using std::result_of to compute
return types for them instead of using decltype directly. That means
Dave's (very convenient!) RETURNS macro shouldn't be used, which is a
bummer. (I think it also means that we need a noexcept_of template that
computes the noexcept of an expression without causing the expression to
appear in the function declaration, but that depends on how
<http://llvm.org/bugs/show_bug.cgi?id=13286> gets resolved.)

All suggestions welcome,

--
Eric Niebler
BoostPro Computing
http://www.boostpro.com

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Marc Glisse
On Fri, 6 Jul 2012, Eric Niebler wrote:

> I'm running into a usability problem with the extended SFINAE rules that
> could effect best practice for all new C++ libraries. I'm hoping some
> clever person in the Boost community can help me.
>
> I find that in highly generic code, using decltype to define the return
> type of functions is very handy. Dave A. suggested a RETURNS macro to
> simplify this idiom:
>
> <https://groups.google.com/d/msg/boost-devel-archive/OzJ5Ft3pSnU/b_Ter9bgNqAJ>
>
> It's used like this:
>
>  template<typename T>
>  auto foo( T && t ) RETURNS( some-expression-using-t );
>
> This is great and I use it a lot. Now, imagine you have a library of
> such functions calling other such functions: function A calls B calls
> ... calls Y, calls Z, all of which use decltype to declare their return
> types. Now, the user calls A passing a variable t for which the
> expression in Z's return type calculation makes no sense. Extended
> SFINAE kicks in, and Z simply disappears. Since Z has disappeared, the
> expression in Y's return type makes no sense, so Y disappears, too. And
> so on up the chain.
>
> The end result is that the user is presented with an error like: "no
> viable function A, template substitution failed." The user is given no
> information about which function in the chain failed to compile, or why.
> This is a serious usability problem!

... in your compiler.

> All suggestions welcome,

Use g++.

template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();}
template<class T> auto g(T x)->decltype(h(x)){return h(x);}
template<class T> auto f(T x)->decltype(g(x)){return g(x);}
int main(){
   f(3);
}

e.cc: In function 'int main()':
e.cc:5:6: error: no matching function for call to 'f(int)'
    f(3);
       ^
e.cc:5:6: note: candidate is:
e.cc:3:24: note: template<class T> decltype (g(x)) f(T)
  template<class T> auto f(T x)->decltype(g(x)){return g(x);}
                         ^
e.cc:3:24: note:   template argument deduction/substitution failed:
e.cc: In substitution of 'template<class T> decltype (g(x)) f(T) [with T =
int]':
e.cc:5:6:   required from here
e.cc:3:24: error: no matching function for call to 'g(int&)'
e.cc:3:24: note: candidate is:
e.cc:2:24: note: template<class T> decltype (h(x)) g(T)
  template<class T> auto g(T x)->decltype(h(x)){return h(x);}
                         ^
e.cc:2:24: note:   template argument deduction/substitution failed:
e.cc: In substitution of 'template<class T> decltype (h(x)) g(T) [with T =
int]':
e.cc:3:24:   required by substitution of 'template<class T> decltype
(g(x)) f(T) [with T = int]'
e.cc:5:6:   required from here
e.cc:2:24: error: no matching function for call to 'h(int&)'
e.cc:2:24: note: candidate is:
e.cc:1:24: note: template<class T> decltype (x.smurf()) h(T)
  template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();}
                         ^
e.cc:1:24: note:   template argument deduction/substitution failed:
e.cc: In substitution of 'template<class T> decltype (x.smurf()) h(T)
[with T = int]':
e.cc:2:24:   required by substitution of 'template<class T> decltype
(h(x)) g(T) [with T = int]'
e.cc:3:24:   required by substitution of 'template<class T> decltype
(g(x)) f(T) [with T = int]'
e.cc:5:6:   required from here
e.cc:1:24: error: request for member 'smurf' in 'x', which is of non-class
type 'int'

--
Marc Glisse

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Andrew Sutton-3
In reply to this post by Eric Niebler-3
> So, my question is: what can be done about this? Can we get the benefit
> of automatic type deduction in return types without causing SFINAE to
> drop functions from the overload set? Is there another trick we can use
> to report errors meaningfully when APIs are misused?

No!

Just kidding. I was actually wondering something very similar earlier
today. How can we intelligently document the causes of SFINAE-based
overloading errors? I don't have a good answer yet.

But I plugged in the problem you mentioned and checked it out with GCC
4.8 (compiled from trunk a month ago -- has it been so long?).


template <typename T>
  auto h(T x) -> decltype(*x) { return *x; }

template <typename T>
  auto g(T x) -> decltype(h(x)) { return h(x); }

template <typename T>
  auto f(T x) -> decltype(g(x)) { return g(x); };

int main()
{
  f(0);
}

And get this as output:

foo.cpp: In function ‘int main()’:
foo.cpp:22:6: error: no matching function for call to ‘f(int)’
   f(0);
      ^
foo.cpp:22:6: note: candidate is:
foo.cpp:18:8: note: template<class T> decltype (g(x)) f(T)
   auto f(T x) -> decltype(g(x)) { return g(x); };
        ^
foo.cpp:18:8: note:   template argument deduction/substitution failed:
foo.cpp: In substitution of ‘template<class T> decltype (g(x)) f(T)
[with T = int]’:
foo.cpp:22:6:   required from here
foo.cpp:18:8: error: no matching function for call to ‘g(int&)’
foo.cpp:18:8: note: candidate is:
foo.cpp:15:8: note: template<class T> decltype (h(x)) g(T)
   auto g(T x) -> decltype(h(x)) { return h(x); }
        ^
foo.cpp:15:8: note:   template argument deduction/substitution failed:
foo.cpp: In substitution of ‘template<class T> decltype (h(x)) g(T)
[with T = int]’:
foo.cpp:18:8:   required by substitution of ‘template<class T>
decltype (g(x)) f(T) [with T = int]’
foo.cpp:22:6:   required from here
foo.cpp:15:8: error: no matching function for call to ‘h(int&)’
foo.cpp:15:8: note: candidate is:
foo.cpp:12:8: note: template<class T> decltype (* x) h(T)
   auto h(T x) -> decltype(*x) { return *x; }
        ^
foo.cpp:12:8: note:   template argument deduction/substitution failed:
foo.cpp: In substitution of ‘template<class T> decltype (* x) h(T)
[with T = int]’:
foo.cpp:15:8:   required by substitution of ‘template<class T>
decltype (h(x)) g(T) [with T = int]’
foo.cpp:18:8:   required by substitution of ‘template<class T>
decltype (g(x)) f(T) [with T = int]’
foo.cpp:22:6:   required from here
foo.cpp:12:27: error: invalid type argument of unary ‘*’ (have ‘int’)
   auto h(T x) -> decltype(*x) { return *x; }


So GCC at least is documenting the root cause of the error. It would
still be nice to customize error reporting for certain classes of
failures in an API.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Andrew Sutton-3
In reply to this post by Marc Glisse
> Use g++.
>
> template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();}
> template<class T> auto g(T x)->decltype(h(x)){return h(x);}
> template<class T> auto f(T x)->decltype(g(x)){return g(x);}

Apparently I should have updated Gmail before hitting send. Looks very
familiar :)

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Eric Niebler-3
In reply to this post by Marc Glisse
On 7/6/2012 12:54 PM, Marc Glisse wrote:

> On Fri, 6 Jul 2012, Eric Niebler wrote:
>> The end result is that the user is presented with an error like: "no
>> viable function A, template substitution failed." The user is given no
>> information about which function in the chain failed to compile, or why.
>> This is a serious usability problem!
>
> ... in your compiler.
>
>> All suggestions welcome,
>
> Use g++.
<snip informative backtrace>

Ah! Thanks. I'm using clang. I agree, it's a compiler QoI issue. I'll
follow up with them.

--
Eric Niebler
BoostPro Computing
http://www.boostpro.com

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Doug Gregor
On Fri, Jul 6, 2012 at 1:23 PM, Eric Niebler <[hidden email]> wrote:

> On 7/6/2012 12:54 PM, Marc Glisse wrote:
>> On Fri, 6 Jul 2012, Eric Niebler wrote:
>>> The end result is that the user is presented with an error like: "no
>>> viable function A, template substitution failed." The user is given no
>>> information about which function in the chain failed to compile, or why.
>>> This is a serious usability problem!
>>
>> ... in your compiler.
>>
>>> All suggestions welcome,
>>
>> Use g++.
> <snip informative backtrace>
>
> Ah! Thanks. I'm using clang. I agree, it's a compiler QoI issue. I'll
> follow up with them.

FWIW, top-of-tree Clang produces:

t.cpp:12:3: error: no matching function for call to 'f'
  f(0);
  ^
t.cpp:8:8: note: candidate template ignored: substitution failure
      [with T = int]: no matching function for call to 'g'
  auto f(T x) -> decltype(g(x)) { return g(x); };
       ^                  ~

Which could probably still be improved, although I can't convince
myself that GCC 4.8 is doing us favors by producing the complete
SFINAE backtrace.

  - Doug

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Nathan Ridge

> From: [hidden email]
> On Fri, Jul 6, 2012 at 1:23 PM, Eric Niebler <[hidden email]> wrote:
> > On 7/6/2012 12:54 PM, Marc Glisse wrote:
> >> On Fri, 6 Jul 2012, Eric Niebler wrote:
> >>> The end result is that the user is presented with an error like: "no
> >>> viable function A, template substitution failed." The user is given no
> >>> information about which function in the chain failed to compile, or why.
> >>> This is a serious usability problem!
> >>
> >> ... in your compiler.
> >>
> >>> All suggestions welcome,
> >>
> >> Use g++.
> > <snip informative backtrace>
> >
> > Ah! Thanks. I'm using clang. I agree, it's a compiler QoI issue. I'll
> > follow up with them.
>
> FWIW, top-of-tree Clang produces:
>
> t.cpp:12:3: error: no matching function for call to 'f'
> f(0);
> ^
> t.cpp:8:8: note: candidate template ignored: substitution failure
> [with T = int]: no matching function for call to 'g'
> auto f(T x) -> decltype(g(x)) { return g(x); };
> ^ ~
>
> Which could probably still be improved, although I can't convince
> myself that GCC 4.8 is doing us favors by producing the complete
> SFINAE backtrace.

I think that as a matter of principle, more information is better than
less. The raw error message can relatively easily be parsed by a tool
that filters out undesired detail, but if the detail is not emitted in
the first place, it's gone.

Regards,
Nate
     

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Eric Niebler-3
On 7/6/2012 5:43 PM, Nathan Ridge wrote:

>
>> From: [hidden email]
>> On Fri, Jul 6, 2012 at 1:23 PM, Eric Niebler <[hidden email]> wrote:
>>> On 7/6/2012 12:54 PM, Marc Glisse wrote:
>>>> On Fri, 6 Jul 2012, Eric Niebler wrote:
>>>>> The end result is that the user is presented with an error like: "no
>>>>> viable function A, template substitution failed." The user is given no
>>>>> information about which function in the chain failed to compile, or why.
>>>>> This is a serious usability problem!
>>>>
>>>> ... in your compiler.
>>>>
>>>>> All suggestions welcome,
>>>>
>>>> Use g++.
>>> <snip informative backtrace>
>>>
>>> Ah! Thanks. I'm using clang. I agree, it's a compiler QoI issue. I'll
>>> follow up with them.
>>
>> FWIW, top-of-tree Clang produces:
>>
>> t.cpp:12:3: error: no matching function for call to 'f'
>> f(0);
>> ^
>> t.cpp:8:8: note: candidate template ignored: substitution failure
>> [with T = int]: no matching function for call to 'g'
>> auto f(T x) -> decltype(g(x)) { return g(x); };
>> ^ ~
>>
>> Which could probably still be improved, although I can't convince
>> myself that GCC 4.8 is doing us favors by producing the complete
>> SFINAE backtrace.
>
> I think that as a matter of principle, more information is better than
> less. The raw error message can relatively easily be parsed by a tool
> that filters out undesired detail, but if the detail is not emitted in
> the first place, it's gone.

I'd like to second what Nate said. My experience is that not providing
the backtrace is a usability nightmare. To me, this is *exactly*
analogous to a template instantiation backtrace and should, IMO, be
handled exactly the same way. If you want to snip the middle of the
backtrace, fine, but show the start and the end (the deepest point, I
guess), and perhaps provide an option for dumping the complete thing.

My context is this: I'm rewriting proto in C++11. Entire expression tree
transforms are function call chains like this. To simply be told, "nope,
that transform can't be applied" but not why is maddening.

--
Eric Niebler
BoostPro Computing
http://www.boostpro.com

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Nathan Ridge

<aside>

> I'm rewriting proto in C++11.

Nice!

</aside>

Regards,
Nate
     

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Doug Gregor
In reply to this post by Eric Niebler-3
On Fri, Jul 6, 2012 at 6:17 PM, Eric Niebler <[hidden email]> wrote:

> On 7/6/2012 5:43 PM, Nathan Ridge wrote:
>>
>>> From: [hidden email]
>>> On Fri, Jul 6, 2012 at 1:23 PM, Eric Niebler <[hidden email]> wrote:
>>>> On 7/6/2012 12:54 PM, Marc Glisse wrote:
>>>>> On Fri, 6 Jul 2012, Eric Niebler wrote:
>>>>>> The end result is that the user is presented with an error like: "no
>>>>>> viable function A, template substitution failed." The user is given no
>>>>>> information about which function in the chain failed to compile, or why.
>>>>>> This is a serious usability problem!
>>>>>
>>>>> ... in your compiler.
>>>>>
>>>>>> All suggestions welcome,
>>>>>
>>>>> Use g++.
>>>> <snip informative backtrace>
>>>>
>>>> Ah! Thanks. I'm using clang. I agree, it's a compiler QoI issue. I'll
>>>> follow up with them.
>>>
>>> FWIW, top-of-tree Clang produces:
>>>
>>> t.cpp:12:3: error: no matching function for call to 'f'
>>> f(0);
>>> ^
>>> t.cpp:8:8: note: candidate template ignored: substitution failure
>>> [with T = int]: no matching function for call to 'g'
>>> auto f(T x) -> decltype(g(x)) { return g(x); };
>>> ^ ~
>>>
>>> Which could probably still be improved, although I can't convince
>>> myself that GCC 4.8 is doing us favors by producing the complete
>>> SFINAE backtrace.
>>
>> I think that as a matter of principle, more information is better than
>> less. The raw error message can relatively easily be parsed by a tool
>> that filters out undesired detail, but if the detail is not emitted in
>> the first place, it's gone.
>
> I'd like to second what Nate said. My experience is that not providing
> the backtrace is a usability nightmare. To me, this is *exactly*
> analogous to a template instantiation backtrace and should, IMO, be
> handled exactly the same way. If you want to snip the middle of the
> backtrace, fine, but show the start and the end (the deepest point, I
> guess), and perhaps provide an option for dumping the complete thing.

The typical complaints about template error messages concern excess
verbosity rather than a lack of detail, so simply providing the full
backtrace in all cases is not necessarily helpful. A more nuanced
approach that provides this detail when it is interesting (e.g., only
one candidate needs it) would be ideal; failing that, some
command-line options to dial up the verbosity would allow experts to
get the information they need while not flooding the terminals of the
majority of C++ programmers.

This is certainly worth of a Clang bug: http://llvm.org/bugs/

  - Doug

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Felipe Magno de Almeida
In reply to this post by Eric Niebler-3
On Fri, Jul 6, 2012 at 10:17 PM, Eric Niebler <[hidden email]> wrote:
>

[snip]

> I'd like to second what Nate said. My experience is that not providing
> the backtrace is a usability nightmare. To me, this is *exactly*
> analogous to a template instantiation backtrace and should, IMO, be
> handled exactly the same way. If you want to snip the middle of the
> backtrace, fine, but show the start and the end (the deepest point, I
> guess), and perhaps provide an option for dumping the complete thing.

+1

I have this problem all the time with skipped template instantiations
in error messages in GCC. Sometimes I just can't know which code
causes the problem because its instantiation isn't listed in the error
message. This is *very annoying* and I just surrender and try with
MSVC to find the root cause of the problem. It is not always the
leaf, nor is always the first instantation as well.

[snip]

> --
> Eric Niebler
> BoostPro Computing
> http://www.boostpro.com

Regards,
--
Felipe Magno de Almeida

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Eric Niebler-3
In reply to this post by Doug Gregor
On 7/9/2012 8:42 AM, Doug Gregor wrote:

> On Fri, Jul 6, 2012 at 6:17 PM, Eric Niebler <[hidden email]> wrote:
>> I'd like to second what Nate said. My experience is that not providing
>> the backtrace is a usability nightmare. To me, this is *exactly*
>> analogous to a template instantiation backtrace and should, IMO, be
>> handled exactly the same way. If you want to snip the middle of the
>> backtrace, fine, but show the start and the end (the deepest point, I
>> guess), and perhaps provide an option for dumping the complete thing.
>
> The typical complaints about template error messages concern excess
> verbosity rather than a lack of detail, so simply providing the full
> backtrace in all cases is not necessarily helpful.

OK, agreed.

> A more nuanced
> approach that provides this detail when it is interesting (e.g., only
> one candidate needs it)

Not sure what you mean by "only one candidate needs it". I think there
will be a challenge reporting these failures when there are overload
sets in the call tree. You probably don't want to report the reason for
failure for every overload at every level of the tree -- at least not by
default.

> would be ideal; failing that, some
> command-line options to dial up the verbosity would allow experts to
> get the information they need while not flooding the terminals of the
> majority of C++ programmers.

Yes, please. We don't want to have to tell people to try compiling with
gcc if they don't understand clang's errors. :-P

> This is certainly worth of a Clang bug: http://llvm.org/bugs/

http://llvm.org/bugs/show_bug.cgi?id=13309

--
Eric Niebler
BoostPro Computing
http://www.boostpro.com

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Mathias Gaunard-2
On 09/07/2012 21:32, Eric Niebler wrote:

> Yes, please. We don't want to have to tell people to try compiling with
> gcc if they don't understand clang's errors. :-P

I've already been doing just that for a while. Clang error messages are
not as good as they claim.

The macro expansion, in particular, can easily lead to ridiculously
verbose error messages.


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Mathias Gaunard-2
In reply to this post by Felipe Magno de Almeida
On 09/07/2012 17:58, Felipe Magno de Almeida wrote:

> I have this problem all the time with skipped template instantiations
> in error messages in GCC. Sometimes I just can't know which code
> causes the problem because its instantiation isn't listed in the error
> message. This is *very annoying* and I just surrender and try with
> MSVC to find the root cause of the problem. It is not always the
> leaf, nor is always the first instantation as well.

The next GCC version (4.8) supports the -ftemplate-backtrace-limit=0
option to disable the limit.

Clang supports it as well.


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Eric Niebler-3
In reply to this post by Andrew Sutton-3
On 7/6/2012 1:15 PM, Andrew Sutton wrote:

>> So, my question is: what can be done about this? Can we get the benefit
>> of automatic type deduction in return types without causing SFINAE to
>> drop functions from the overload set? Is there another trick we can use
>> to report errors meaningfully when APIs are misused?
>
> No!
>
> Just kidding. I was actually wondering something very similar earlier
> today. How can we intelligently document the causes of SFINAE-based
> overloading errors? I don't have a good answer yet.
I spent some time playing with this today. Attached is my best shot. The
basic idea is to change the RETURNS macro to -- in addition to declaring
the trailing return type and the function body -- also define an
implicit conversion to a function pointer. That gets selected as the
function called in the case that sfinae fails. That function returns a
sfinae_error that contains some useful information: the text of the
expression that failed to compile, the file and line number of the
sfinae failure, and the types of the function parameters (mangled,
unfortunately).

It's used like this:

struct S0 {
    template<typename T>
    auto operator()(T t) const RETURN( (t), t + 1 )
};

The first macro parameter is the function parameters. The second is the
expression to evaluate.

If all your function objects are declared this way, you can call them as
normal. But if you get a sfinae failure anywhere in the call chain, the
result of the call is a sfinae_error object with the goods.

Can anybody do better?

--
Eric Niebler
BoostPro Computing
http://www.boostpro.com


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

sfinae_error.cpp (4K) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Paul Fultz II


> I spent some time playing with this today. Attached is my best shot. The
> basic idea is to change the RETURNS macro to -- in addition to declaring
> the trailing return type and the function body -- also define an
> implicit conversion to a function pointer. That gets selected as the
> function called in the case that sfinae fails. That function returns a
> sfinae_error that contains some useful information: the text of the
> expression that failed to compile, the file and line number of the
> sfinae failure, and the types of the function parameters (mangled,
> unfortunately).
>
Unfortunately, this results in a run-time error instead of compile-time.

> Can anybody do better?

Attached is code that does present a compile-time error. It uses a function
adaptor called `sfinae_error`. This will force sfinae success on the expression
type dedcution by using a fail-through type. The fail through type doesn't
matter since we know the function call will produce a compile error either way.

So now, the `S0` class can be defined like this:

    struct S0_sfinae {
        template<typename T>
        auto operator()(T t) const RETURN( t + 1 )
    };

    typedef sfinae_error<S0_sfinae> S0;

Then when you try to call it like this:

    struct foo {};
    S2()(foo());

Clang will output the error inside of `sfinae_error` function adaptor with a
full backtrace. At the bottom it will say:

    note: candidate template ignored: substitution failure
          [with T = foo]: invalid operands to binary expression ('foo' and 'int')
        auto operator()(T t) const RETURN( t + 1 )

Which is what you want. Futhermore, this can be used even when there are
multiple overloads in the `S0` class.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

sfinae_error.cpp (2K) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Eric Niebler-3
On 8/11/2012 6:23 PM, paul Fultz wrote:

>
>
>> I spent some time playing with this today. Attached is my best shot. The
>> basic idea is to change the RETURNS macro to -- in addition to declaring
>> the trailing return type and the function body -- also define an
>> implicit conversion to a function pointer. That gets selected as the
>> function called in the case that sfinae fails. That function returns a
>> sfinae_error that contains some useful information: the text of the
>> expression that failed to compile, the file and line number of the
>> sfinae failure, and the types of the function parameters (mangled,
>> unfortunately).
>>
> Unfortunately, this results in a run-time error instead of compile-time.

Right. It can be easily turned into a compile-time error by
static_assert'ing that the function's return type is not sfinae_error,
but the compiler error wouldn't be informative. You'd have to run to
code to see the error.

I was hoping to have a way to transport the error to an API boundary and
reporting it there, instead of presenting users with an imposing
template instantiation backtrace, but that might not be possible.

>> Can anybody do better?
>
> Attached is code that does present a compile-time error. It uses a function
> adaptor called `sfinae_error`. This will force sfinae success on the expression
> type dedcution by using a fail-through type. The fail through type doesn't
> matter since we know the function call will produce a compile error either way.
>
> So now, the `S0` class can be defined like this:
>
>     struct S0_sfinae {
>         template<typename T>
>         auto operator()(T t) const RETURN( t + 1 )
>     };
>
>     typedef sfinae_error<S0_sfinae> S0;
>
> Then when you try to call it like this:
>
>     struct foo {};
>     S2()(foo());
>
> Clang will output the error inside of `sfinae_error` function adaptor with a
> full backtrace. At the bottom it will say:
>
>     note: candidate template ignored: substitution failure
>           [with T = foo]: invalid operands to binary expression ('foo' and 'int')
>         auto operator()(T t) const RETURN( t + 1 )
>
> Which is what you want. Futhermore, this can be used even when there are
> multiple overloads in the `S0` class.

Great! And very simple. It doesn't move the error close to the API
boundary, but other than that, it fits the bill. Thanks. If I end up
using this technique in my code, I will credit you.

--
Eric Niebler
BoostPro Computing
http://www.boostpro.com

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

John Maddock-3
> Right. It can be easily turned into a compile-time error by
> static_assert'ing that the function's return type is not sfinae_error,
> but the compiler error wouldn't be informative. You'd have to run to
> code to see the error.
>
> I was hoping to have a way to transport the error to an API boundary and
> reporting it there, instead of presenting users with an imposing
> template instantiation backtrace, but that might not be possible.

I haven't followed this discussion, but you can move static_assert's up the
call stack by moving the condition into an enable/disable_if, then the
compiler simply doesn't find the function and you get the error "sooner".

But you've probably considered all this already yours, John.


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Eric Niebler-3
On 8/13/2012 12:46 AM, John Maddock wrote:

>> Right. It can be easily turned into a compile-time error by
>> static_assert'ing that the function's return type is not sfinae_error,
>> but the compiler error wouldn't be informative. You'd have to run to
>> code to see the error.
>>
>> I was hoping to have a way to transport the error to an API boundary and
>> reporting it there, instead of presenting users with an imposing
>> template instantiation backtrace, but that might not be possible.
>
> I haven't followed this discussion, but you can move static_assert's up
> the call stack by moving the condition into an enable/disable_if, then
> the compiler simply doesn't find the function and you get the error
> "sooner".
>
> But you've probably considered all this already yours, John.

John, what you're describing is precisely what we're trying to avoid: a
mysterious "cannot find function" error because the function has been
SFINAE'd out ... but where and why? The programmer needs better feedback
than that.

--
Eric Niebler
BoostPro Computing
http://www.boostpro.com

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: C++11 decltype/SFINAE puzzler

Eric Niebler-3
In reply to this post by Eric Niebler-3
On 8/12/2012 7:50 PM, Eric Niebler wrote:
> Great! And very simple. It doesn't move the error close to the API
> boundary, but other than that, it fits the bill. Thanks. If I end up
> using this technique in my code, I will credit you.

I think I have a very nice solution now. It combines my earlier approach
with with Paul Fritz's. See the attached.

You define your callables with the RETURNS macro and a try_call function
object wrapper, like this:

    struct S0
    {
        template<typename T>
        auto operator()(T t) const RETURN( t + 1 )
    };

    struct S1
    {
        template<typename T>
        auto operator()(T t) const RETURN( try_call<S0>()(t) )
    };

    struct S2
    {
        template<typename T>
        auto operator()(T t) const RETURN( try_call<S1>()(t) )
    };

Then, you simply invoke your function object:

    auto i = S2()(32);     // ok
    auto x = S2()(foo());  // compile-time error

The compile error is transported to the API boundary. The result is a
very short and precise error message about the cause of the failure.
Clang gives this:

> $ /usr/local/bin/clang++ -std=gnu++11 sfinae_error.cpp
> sfinae_error.cpp:28:26: error: no matching function for call to object of type 'S0'
>         typedef decltype(std::declval<Fun>()(std::declval<Args>()...)) type;
>                          ^~~~~~~~~~~~~~~~~~~
> sfinae_error.cpp:91:14: note: in instantiation of member function 'sfinae_error<S0 (foo &)>::what'
>       requested here
>     auto x = S2()(foo());
>              ^
> sfinae_error.cpp:70:10: note: candidate template ignored: substitution failure [with T = foo]:
>       invalid operands to binary expression ('foo' and 'int')
>     auto operator()(T t) const RETURN( t + 1 )
>          ^                     ~~~~~~~~~~~~~~~
> 1 error generated.
Note that no information about the intermediate calls (S2, S1) shows up
in the backtrace. Just the error you care about.

GCC-4.7 gives a similarly terse error message. This, I think, is what
I've been looking for.

--
Eric Niebler
BoostPro Computing
http://www.boostpro.com


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

sfinae_error.cpp (3K) Download Attachment
12
Loading...