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.