C++:从可变参数模板创建自定义函数调度程序

gim*_*ilk 3 c++ templates variadic

我有一些从序列化数据中读取各种类型的函数,例如:

class DataDeserializer
{
    int getInt();
    std::string getString();
    MyClass getMyClass();
}
Run Code Online (Sandbox Code Playgroud)

然后我有各种带有任意参数的回调函数,例如:

void callbackA (int, int, int);
void callbackB (int, std::string);
void callbackC (std::string, int, MyClass, int);
Run Code Online (Sandbox Code Playgroud)

我想调用从反序列化数据流中读取的参数的各种回调.我想要的是尽可能自动化样板代码.我想也许我可以使用模板.如果我有某种Dispatcher类,例如:

template <SOMETHING??> class Dispatcher
{
    void dispatch()
    {
        // ???? 
    }

    SOMEFUNCTIONTYPE callback;
    DataDeserializer myDeserializer;
};
Run Code Online (Sandbox Code Playgroud)

然后声明各种特定的调度员:

Dispatcher<int,int,int>                  myDispatcherA (deserializer, callbackA);
Dispatcher<int,std::string>              myDispatcherB (deserializer, callbackB);
Dispatcher<std::string,int,MyClass,int>  myDispatcherC (deserializer, callbackC);
Run Code Online (Sandbox Code Playgroud)

然后,当我想发送时,我只是打电话给:

myDispatcherB.dispatch();
Run Code Online (Sandbox Code Playgroud)

下面会扩展到这样的东西:

void dispatch()
{
    callback (myDeserializer.getString(), myDeserializer.getInt(), myDeserializer.getMyClass(), myDeserializer.getInt());
}
Run Code Online (Sandbox Code Playgroud)

这可能与C++ 11可变参数模板有关吗?我已经阅读了一些关于它们的内容,似乎递归使用了很多.

Xeo*_*Xeo 6

我为stream_function班上做过类似的事.基本思想是将类型传递给函数模板,该模板执行The Right Thing™,并扩展该调用:

callback(magic<Args>(/* sth */)...);
Run Code Online (Sandbox Code Playgroud)

但是,如果您的函数不是纯粹的并且修改了某些状态,并且因此需要以正确的顺序调用它们,则必须使用一些技巧强制执行该命令.

如果你正在使用Clang,这很容易,因为它强制对braced-init-lists进行从左到右的评估.这允许您只使用一个小助手类型

struct invoker{
  template<class F, class... Args>
  invoker(F&& f, Args&&... args){ f(std::forward<Args>(args)...); }
};
Run Code Online (Sandbox Code Playgroud)

然后呢

invoker{ callback, magic<Args>(/* sth */)... };
Run Code Online (Sandbox Code Playgroud)

遗憾的是,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)...); }
};
Run Code Online (Sandbox Code Playgroud)

实例.