You certainly don't need C++17.... the code's just uglier (e.g., constexpr-if goes back to SFINAE, and so on. My approach in particular gets much uglier.)
Mentally I phrased the problem as
"Given any callable, call it with some arguments and see if the result is false. If it's false, throw. Otherwise, return the result."
Your approach is essentially correct (you at least shouldn't catch the exception inside
invoke
), but another requirement is to accept any number of arguments of different types. You can do that too, I suppose:
1 2 3 4 5 6 7 8 9 10 11 12
|
template<typename type_signature, typename...Args>
static type_signature invoke(std::function<type_signature(Args...)> myFunc, Args&&... args)
try {
type_signature success = myFunc(std::forward<Args>(args)...);
if (!success) {
throw std::runtime_error("failure");
}
return success;
} catch (const std::exception& e) {
std::cerr << e.what();
return 0;
}
|
But that's just a contrived version of
std::invoke
that also happens to check the return value. It's a version of
std::invoke
because the above basically calls some function with some arguments; everything gets deduced, hopefully, although unfortunately no implicit conversions between arguments are possible (see
http://coliru.stacked-crooked.com/a/ac2e8c8ffddbef98 ).
JLBorges' solution is very similar to that, but more generic:
std::function
goes away for some generic callable (named FN) and stuff gets perfect-forwarded. Relaxing the signature on the callable means that conversions between arguments are possible (that's just duck typing to see if the arguments match the callable's signature.)
JLBorges' code also uses SFINAE to handle the case where the callable's result is not convertible to
bool
. I copied that feature too, but I no longer think it's a good idea to support that case.
My solution is essentially the same thing with (another level of) higher-order programming.