I have this very nice wrapper, but I'd like it to accept any number of T, S, R, Q, ...
template<typename U, typename T, typename S>
boost::variant<U, std::string> RunSafeFn2(const std::function<U(const T&,const S&)>& f, const std::string& errMsg, const T& a1, const S& a2)
{
try
{
return f(a1,a2);
}
catch (...)
{
return errMsg;
}
}
I tried the below and have been googling about, but man the error messages are cryptic - is what I'm trying to do even possible?
template<typename U, class ... Ts>
boost::variant<U, std::string> RunSafeFnN(const std::function<U(Ts)>& f, const std::string& errMsg, Ts ... ts)
{
try
{
return bind(f, ts...);
}
catch (...)
{
return errMsg;
}
}
You can do what you want, as this simple program shows:
template <class ... Ts>
void foo(std::function<void(Ts...)> f, Ts && ... ts) {
f(std::forward<Ts>(ts)...);
}
int main()
{
std::function<void(int)> f = [] (int i) { std::cerr << "hello " << i; };
foo(f, 5);
return 0;
}
The thing is, that once RunSafeFn2 is a template, you may as well also template the functor itself. There's very little benefit to type erasing the callable when you are already a template. So in practice, it just makes more sense to do:
template <class F, class ... Ts>
void foo(F f, Ts && ... ts) {
f(std::forward<Ts>(ts)...);
}
Which still allows the usage above, but also allows doing:
foo([] (int i) { std::cerr << "hello " << i; }, 5);
Which will also be more efficient since you completely avoid creating a std::function object. To handle the return type, since you're limited to C++11, you could do:
template <class F, class ... Ts>
auto foo(F f, Ts && ... ts) -> boost::variant<decltype(f(std::forward<Ts>(ts)...)), std::string> {
try {
return f(std::forward<Ts>(ts)...);
}
...
}
Edit: let me add one final thought: In C++11 and on, where callables are so easy to create, a much simpler alternative to this is actually to take a callable and no arguments:
template <class F>
auto foo(F f) -> boost::variant<decltype(f()), std::string> {
try {
return f();
}
...
}
That's because it's pretty easy to capture around the arguments that you need. For example, if I had written my original example that way, I could use it by doing:
int i = 5;
foo([&] () { std::cerr << "hello " << i; });
This has pros and cons especially perhaps when dealing with move only types, but if you want to minimize maintenance burden and your use cases are simple, this is a reasonable alternative.
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