I have some functions that read various types from serialized data, eg:
class DataDeserializer
{
int getInt();
std::string getString();
MyClass getMyClass();
}
I then have various callback functions that take arbitrary parameters, eg:
void callbackA (int, int, int);
void callbackB (int, std::string);
void callbackC (std::string, int, MyClass, int);
I want to call the various callbacks with arguments read from the deserialized data stream. What I would like is to automate the boilerplate code as much as possible. I was thinking maybe I could use templates. If I had some sort of Dispatcher class, eg:
template <SOMETHING??> class Dispatcher
{
void dispatch()
{
// ????
}
SOMEFUNCTIONTYPE callback;
DataDeserializer myDeserializer;
};
Then declare various specific dispatchers:
Dispatcher<int,int,int> myDispatcherA (deserializer, callbackA);
Dispatcher<int,std::string> myDispatcherB (deserializer, callbackB);
Dispatcher<std::string,int,MyClass,int> myDispatcherC (deserializer, callbackC);
Then when I want to dispatch, I just call:
myDispatcherB.dispatch();
which underneath would expand to something like this:
void dispatch()
{
callback (myDeserializer.getString(), myDeserializer.getInt(), myDeserializer.getMyClass(), myDeserializer.getInt());
}
Is this possible with C++11 variadic templates? I've read up a little on them, and it seems recursion is used a lot.
I have done something similar for my stream_function
class. The basic idea is that you pass a type to a function template, which does The Right Thing™, and expand that call:
callback(magic<Args>(/* sth */)...);
However, if your functions aren't pure and modify some state, and as such have the requirement that they need to be called in the correct order, you have to force that order with some tricks.
If you're using Clang, this is rather easy, as it forces left-to-right evaluation for braced-init-lists. This allows you to just use a small helper type
struct invoker{
template<class F, class... Args>
invoker(F&& f, Args&&... args){ f(std::forward<Args>(args)...); }
};
and then do
invoker{ callback, magic<Args>(/* sth */)... };
Unfortunately, GCC doesn't yet implement this feature, so one needs to resort to manual order-enforcement. This can be done with a small helper struct which is just a type-list, but allows one to do some useful things:
- see when the pack is empty (
types<>
), and - process
Args
in a head-then-tail recursive fashion
template<class...> struct types{};
template<class... Args>
struct dispatcher{
std::function<void(Args...)> f;
void call(){ _call(types<Args...>{}); }
private:
// take head, produce value from it, pass after other values
template<class Head, class... Tail, class... Vs>
void _call(types<Head, Tail...>, Vs&&... vs){
_call(types<Tail...>{}, std::forward<Vs>(vs)..., get_value<Head>());
}
// no more values to produce, forward to callback function
template<class... Vs>
void _call(types<>, Vs&&... vs){ f(std::forward<Vs>(vs)...); }
};
Something like this can help you
template<typename T>
T get_value(Deserializer&);
template<>
int get_value(Deserializer& d)
{
return d.getInt();
}
template<>
std::string get_value(Deserializer& d)
{
return d.getString();
}
template<typename... Args>
class Dispatcher
{
public:
template<typename Functor>
Dispatcher(Deserializer& d, const Functor& cb) : myDeserializer(d), callback(cb)
{
}
void dispatch()
{
callback(get_value<Args>(myDeserializer)...);
}
private:
std::function<void(Args...)> callback;
Deserializer myDeserializer;
};
来源:https://stackoverflow.com/questions/18847424/c-create-custom-function-dispatcher-from-variadic-template