У меня есть некоторые функции, которые читают различные типы из сериализованных данных, например:
class DataDeserializer
{
int getInt();
std::string getString();
MyClass getMyClass();
}
Затем у меня есть различные функции обратного вызова, которые принимают произвольные параметры, например:
void callbackA (int, int, int);
void callbackB (int, std::string);
void callbackC (std::string, int, MyClass, int);
Я хочу вызвать различные обратные вызовы с аргументами, прочитанными из десериализованного потока данных.
Я хотел бы максимально автоматизировать шаблонный код.
Я думал, может быть, я мог бы использовать шаблоны. Если бы у меня был какой-то Диспетчер
класс, например:
template <SOMETHING??> class Dispatcher
{
void dispatch()
{
// ????
}
SOMEFUNCTIONTYPE callback;
DataDeserializer myDeserializer;
};
Затем объявите различных конкретных диспетчеров:
Dispatcher<int,int,int> myDispatcherA (deserializer, callbackA);
Dispatcher<int,std::string> myDispatcherB (deserializer, callbackB);
Dispatcher<std::string,int,MyClass,int> myDispatcherC (deserializer, callbackC);
Затем, когда я хочу отправить, я просто позвоню:
myDispatcherB.dispatch();
который снизу расширится до чего-то вроде этого:
void dispatch()
{
callback (myDeserializer.getString(), myDeserializer.getInt(), myDeserializer.getMyClass(), myDeserializer.getInt());
}
Возможно ли это с помощью C ++ 11 вариационных шаблонов? Я немного прочитал о них, и кажется, что рекурсия используется очень часто.
Я сделал что-то подобное для моего stream_function
учебный класс. Основная идея заключается в том, что вы передаете тип в шаблон функции, который выполняет The Right Thing ™, и расширяете этот вызов:
callback(magic<Args>(/* sth */)...);
Однако, если ваши функции не являются чистыми и изменяют какое-то состояние и поэтому требуют, чтобы их вызывали в правильном порядке, вы должны принудительно установить этот порядок с помощью некоторых приемов.
Если вы используете Clang, это довольно просто, так как он вызывает оценку слева направо для braced-init-lists. Это позволяет вам просто использовать маленький тип помощника
struct invoker{
template<class F, class... Args>
invoker(F&& f, Args&&... args){ f(std::forward<Args>(args)...); }
};
а затем сделать
invoker{ callback, magic<Args>(/* sth */)... };
К сожалению, GCC пока не реализует эту функцию, поэтому нужно прибегнуть к ручному обеспечению выполнения заказов. Это можно сделать с помощью небольшой вспомогательной структуры, которая является просто списком типов, но позволяет сделать несколько полезных вещей:
types<>
), а такжеArgs
рекурсивным способом голова-хвост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)...); }
};
Нечто подобное может помочь вам
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;
};