
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