在C ++中动态调用注册函数的好方法是什么? [英] What is a good way to register functions for dynamic invocation in C++?

查看:126
本文介绍了在C ++中动态调用注册函数的好方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我目前的设置中,我有一个

In my current setup, I have a

typedef std::function<void (MyClass&, std::vector<std::string>) MyFunction;
std::map<std::string, MyFunction> dispatch_map;

我用一个宏注册我的函数。但是,我有一个问题:参数作为一个向量的字符串,我必须在函数内转换。我宁愿做这个转换外的函数,在调度器级别。这可能吗?

And I register my functions in it with a macro. However, I have a problem with this: the parameters are passed as a vector of strings, which I have to convert inside the functions. I would rather do this conversion outside the functions, at the dispatcher level. Is this possible? The function signatures are known at compile time, and never change at run time.

推荐答案

您可以使用可变参数模板获得更多的功能,一些模板/虚拟技术。使用以下代码,您将能够执行以下操作:

You can get pretty far with variadic templates and some template/virtual techniques. With the following codes, you'll be able to do something like:

std::string select_string (bool cond, std::string a, std::string b) {
    return cond ? a : b;
}

int main () {
    Registry reg;
    reg.set ("select_it", select_string);
    reg.invoke ("select_it", "1 John Wayne"));
    reg.invoke ("select_it", "0 John Wayne"));
}

输出:

John
Wayne






完全实现:



这些代码是示例性的。您应该对其进行优化,以便在参数列表扩展中提供完美的转发少的冗余。


Full implementation:

These codes are exemplary. You should optimize it to provide perfect forwarding less redundancy in parameter list expansion.

标题和测试函数

#include <functional>
#include <string>
#include <sstream>
#include <istream>
#include <iostream>
#include <tuple>

std::string select_string (bool cond, std::string a, std::string b) {
    return cond ? a : b;
}

这有助于我们解析字符串并将结果放入元组:

This helps us parsing a string and putting results into a tuple:

//----------------------------------------------------------------------------------

template <typename Tuple, int Curr, int Max> struct init_args_helper;

template <typename Tuple, int Max>
struct init_args_helper<Tuple, Max, Max> {
    void operator() (Tuple &, std::istream &) {}
};

template <typename Tuple, int Curr, int Max>
struct init_args_helper {
    void operator() (Tuple &tup, std::istream &is) {
        is >> std::get<Curr>(tup);
        return init_args_helper<Tuple, Curr+1, Max>() (tup, is);
    }
};


template <int Max, typename Tuple>
void init_args (Tuple &tup, std::istream &ss)
{
    init_args_helper<Tuple, 0, Max>() (tup, ss);
}

这会展开一个函数指针和一个元组到函数调用by function-pointer):

This unfolds a function pointer and a tuple into a function call (by function-pointer):

//----------------------------------------------------------------------------------

template <int ParamIndex, int Max, typename Ret, typename ...Args>
struct unfold_helper;

template <int Max, typename Ret, typename ...Args>
struct unfold_helper<Max, Max, Ret, Args...> {
    template <typename Tuple, typename ...Params>
    Ret unfold (Ret (*fun) (Args...), Tuple tup, Params ...params)
    {
        return fun (params...);
    }
};

template <int ParamIndex, int Max, typename Ret, typename ...Args>
struct unfold_helper {
    template <typename Tuple, typename ...Params>
    Ret unfold (Ret (*fun) (Args...), Tuple tup, Params ...params)
    {
        return unfold_helper<ParamIndex+1, Max, Ret, Args...> ().
               unfold(fun, tup, params..., std::get<ParamIndex>(tup));
    }
};



template <typename Ret, typename ...Args>
Ret unfold (Ret (*fun) (Args...), std::tuple<Args...> tup) {
    return unfold_helper<0, sizeof...(Args), Ret, Args...> ().unfold(fun, tup);
}

此函数将它放在一起

//----------------------------------------------------------------------------------

template <typename Ret, typename ...Args>
Ret foo (Ret (*fun) (Args...), std::string mayhem) {

    // Use a stringstream for trivial parsing.
    std::istringstream ss;
    ss.str (mayhem);

    // Use a tuple to store our parameters somewhere.
    // We could later get some more performance by combining the parsing
    // and the calling.
    std::tuple<Args...> params;
    init_args<sizeof...(Args)> (params, ss);

    // This demondstrates expanding the tuple to full parameter lists.
    return unfold<Ret> (fun, params);
}

这是我们的测试:

int main () {
    std::cout << foo (select_string, "0 John Wayne") << '\n';
    std::cout << foo (select_string, "1 John Wayne") << '\n';
}


Warning: Code needs more verification upon parsing and should use std::function<> instead of naked function pointer


Based on above code, it is simple to write a function-registry:

class FunMeta {
public:
    virtual ~FunMeta () {}
    virtual boost::any call (std::string args) const = 0;
};

template <typename Ret, typename ...Args>
class ConcreteFunMeta : public FunMeta {
public:
    ConcreteFunMeta (Ret (*fun) (Args...)) : fun(fun) {}

    boost::any call (std::string args) const {
        // Use a stringstream for trivial parsing.
        std::istringstream ss;
        ss.str (args);

        // Use a tuple to store our parameters somewhere.
        // We could later get some more performance by combining the parsing
        // and the calling.
        std::tuple<Args...> params;
        init_args<sizeof...(Args)> (params, ss);

        // This demondstrates expanding the tuple to full parameter lists.
        return unfold<Ret> (fun, params);
    }

private:
    Ret (*fun) (Args...);
};

class Registry {
public:
    template <typename Ret, typename ...Args>
    void set (std::string name, Ret (*fun) (Args...)) {
        funs[name].reset (new ConcreteFunMeta<Ret, Args...> (fun));
    }

    boost::any invoke (std::string name, std::string args) const {
        const auto it = funs.find (name);
        if (it == funs.end())
            throw std::runtime_error ("meh");
        return it->second->call (args);
    }

private:
    // You could use a multimap to support function overloading.
    std::map<std::string, std::shared_ptr<FunMeta>> funs;
};

甚至可以想到支持函数重载,使用多重映射和基于什么内容

One could even think of supporting function overloading with this, using a multimap and dispatching decisions based on what content is on the passed arguments.

以下是使用方法:

int main () {
    Registry reg;
    reg.set ("select_it", select_string);
    std::cout << boost::any_cast<std::string> (reg.invoke ("select_it", "0 John Wayne")) << '\n'
              << boost::any_cast<std::string> (reg.invoke ("select_it", "1 John Wayne")) << '\n';
}

这篇关于在C ++中动态调用注册函数的好方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆