I have a wrapper function in C++ 11, designed to be used with lambdas, like so:
template<typename Func>
int WrapExceptions(Func&& f)
{
try
{
return f();
}
catch(std::exception)
{
return -1;
}
}
And I can call it like so:
int rc = WrapExceptions([&]{
DoSomething();
return 0;
});
assert(rc == 0);
And life is OK. What I want to do, though, is overload or specialize the wrapper function such that when the inner function returns void, the outer function returns a default value of 0, e.g.:
int rc = WrapExceptions([&]{
DoSomething();
});
assert(rc == 0);
Can I actually do this in C++ 11? I cannot for the life of me think of how.
To do so, we can use a function template specialization (sometimes called a full or explicit function template specialization) to create a specialized version of the print() function for type double.
Explanation: Function template is used to create a function without having to specify the exact type. 2. Which is used to describe the function using placeholder types? Explanation: During runtime, We can choose the appropriate type for the function and it is called as template type parameters.
Template in C++is a feature. We write code once and use it for any data type including user defined data types. For example, sort() can be written and used to sort any data type items. A class stack can be created that can be used as a stack of any data type.
Defining a Function Template In the above code, T is a template argument that accepts different data types ( int , float , etc.), and typename is a keyword. When an argument of a data type is passed to functionName() , the compiler generates a new version of functionName() for the given data type.
You may 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; }
}
Maybe a bit over-engineered, but you can use tag dispatch:
#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, implement a shorter dispatch:
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; }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With