【问题标题】:C++ : create custom function dispatcher from variadic templateC++:从可变参数模板创建自定义函数调度程序
【发布时间】:2013-09-17 10:30:39
【问题描述】:

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

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:

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 可变参数模板可以做到这一点吗?我已经阅读了一些关于它们的内容,似乎递归使用了很多。

【问题讨论】:

  • 从哪里获得字符串、int、some-class 和 int,例如,从哪里获得?我的stream_function(找到here)可能会有所帮助。
  • 已编辑以使反序列化器的使用更加清晰。反序列化器本身就是黑盒代码。

标签: c++ templates variadic


【解决方案1】:

我为我的stream_function 班级做了类似的事情。基本思想是将类型传递给执行 The Right Thing™ 的函数模板,然后扩展该调用:

callback(magic<Args>(/* sth */)...);

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

如果您使用的是 Clang,这相当容易,因为它强制对括号初始化列表进行从左到右的评估。这允许您只使用一个小的帮助类型

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

然后做

invoker{ callback, magic<Args>(/* sth */)... };

不幸的是,GCC 还没有实现这一功能,所以需要求助于手动执行命令。这可以通过一个小型辅助结构来完成,它只是一个类型列表,但允许人们做一些有用的事情:

  • 查看包装何时为空 (types&lt;&gt;),并且
  • 以头尾递归方式处理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)...); }
};

Live example.

【讨论】:

    【解决方案2】:

    这样的事情可以帮助你

    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;
    };
    

    Live example

    【讨论】:

    • 请注意,这与我的 stream_function 有相同的问题 - get_value 调用的评估顺序未指定。
    • 是的,这几乎正是我正在寻找的,但我发现这些参数似乎是以相反的顺序传递的。有没有办法保证评估顺序?
    • @gimmeamilk:如果您使用的是 Clang,请参阅我在之前的评论中链接的答案。如果您使用的是 GCC,则需要手动构建包并递归地一次调用一个函数。
    • @Xeo 不幸的是它是 gcc。我不确定递归解决方案是什么。我可以在递归之上一次调用每个 get_value(),但是如何同时将所有值传递给回调?
    • @gimmeamilk:您可以将它们聚合在一个可变参数包中。 Here 就是一个例子。
    猜你喜欢
    • 2014-11-04
    • 1970-01-01
    • 1970-01-01
    • 2012-04-15
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多