In C ++ 11, how do I specialize a function template that accepts a function object based on the return type?

I have a wrapper function in C ++ 11 designed for use with lambdas, for example:

template<typename Func> int WrapExceptions(Func&& f) { try { return f(); } catch(std::exception) { return -1; } } 

And I can call it that:

 int rc = WrapExceptions([&]{ DoSomething(); return 0; }); assert(rc == 0); 

And life is fine. However, I want to overload or specialize the wrapper function, so when the inner function returns void, the outer function returns the default value of 0, for example:

 int rc = WrapExceptions([&]{ DoSomething(); }); assert(rc == 0); 

Can I do this in C ++ 11? I can’t think of how for life.

+5
source share
2 answers

You can use SFINAE:

  • with std::result_of

     template<typename Func> typename std::enable_if< std::is_convertible<typename std::result_of<Func()>::type, int>::value, int >::type WrapExceptions(Func&& f) { try { return f(); } catch(std::exception) { return -1; } } template<typename Func> typename std::enable_if< std::is_same<void, typename std::result_of<Func()>::type>::value, int >::type WrapExceptions(Func&& f) { try { f(); return 0; /* default value */ } catch(std::exception) { return -1; } } 
  • with decltype :

     template<typename Func> auto WrapExceptions(Func&& f) -> typename std::enable_if< std::is_convertible<decltype(f()), int>::value, int >::type { try { return f(); } catch(std::exception) { return -1; } } template<typename Func> auto WrapExceptions(Func&& f) -> typename std::enable_if< std::is_same<void, decltype(f())>::value, int >::type { try { f(); return 0; } catch(std::exception) { return -1; } } 
+7
source

Perhaps a bit overhauled, but you can use tagging:

 #include <stdexcept> #include <type_traits> #include <utility> namespace detail { struct returns_convertible_to_int {}; struct returns_void {}; template<typename Func> int WrapException_dispatch(Func&& f, returns_convertible_to_int) { return f(); } template<typename Func> int WrapException_dispatch(Func&& f, returns_void) { f(); return 0; } template<typename T, typename dummy = void> struct dispatch { static_assert(std::is_same<T, void>::value, "Incompatible return type"); }; template<typename T> struct dispatch<T, typename std::enable_if< std::is_convertible<T, int>{} >::type> { using type = returns_convertible_to_int; }; template<typename T> struct dispatch<T, typename std::enable_if< std::is_same<T, void>{} >::type> // alt: template<> struct dispatch<void,void> { using type = returns_void; }; } template<typename Func> int WrapException(Func&& f) { try { return detail::WrapException_dispatch( std::forward<Func>(f), typename detail::dispatch<decltype(f())>::type{} ); } catch(std::exception const&) { return -1; } } 

Usage example:

 int foo() { return 42; } void bar() {} int main() { WrapException(foo); WrapException(bar); } 

You can, of course, do a shorter sending:

 namespace detail { template<typename Func> auto WrapException_dispatch(Func&& f, int) -> typename std::enable_if< std::is_convertible<decltype(f()), int>::value, int >::type { return f(); } template<typename Func> int WrapException_dispatch(Func&& f, ...) { f(); return 0; } } template<typename Func> int WrapException(Func&& f) { try { return detail::WrapException_dispatch( std::forward<Func>(f), 0 ); } catch(std::exception const&) { return -1; } } 
+4
source

Source: https://habr.com/ru/post/1202378/


All Articles