I'd like to be able to enforce a particular callback signature when using a templated callback parameter. The goals are three-fold:
- Get a compiler error at the user call site, not deep inside the method accepting the callback.
- Obey the least surprise principle and prevent the user from providing a callback that returns a value when the implementation using the callback will not check its value etc (exact signature match)
- Use idiomatic c++ and best practices as much as possible. Be efficient.
Coming from C#, I first attempted to mimic the Action and Func classes like so:
template<typename ...TArgs> using Action = std::function<void(TArgs...)>;
template<typename TRet, typename ...TArgs> using Func = std::function<TRet(TArgs...)>;
This works ok, except it uses std::function which I've heard to avoid for this 'simple' task (fails #3). Also, unfortunately, it fails my #2 requirement above as well:
void DoActionStuff(Action<int, float> callback)
{
callback(1, 2.0);
}
void DoFuncStuff(Func<float, int, float> callback)
{
float result = callback(1, 2.0);
if (result > 100.0) { throw runtime_error("derp"); };
}
DoActionStuff([](int x, float y) { cout << x << " " << y << endl; }); // OK
DoActionStuff([](int x, float y) { cout << x << " " << y << endl; return x * y; }); // No error :( Why not?
DoFuncStuff([](int x, float y) { cout << x << " " << y << endl; }); // OK - We get a compiler error right here at the call-site for this one...
DoFuncStuff([](int x, float y) { cout << x << " " << y << endl; return x * y; }); // OK
How do I meet all 3 requirements doing something like the following? Is there any, nice, trait magic I could use to provide a better signature for this method to convey exactly what type of callback is required?
template<typename TCallback>
void DoStuff(TCallback callback) { ... } // Unnecessarily burdens the client of this api (they have no idea the signature to use here)