无论参数签名如何,我都可以创建一个函数表吗?

时间:2014-10-22 16:56:18

标签: c++ c++11 lambda

我想从一个表中调用所有类型的函数。 (考虑返回类型都是无效的)

为了说明我在说什么,这里有一些显然不起作用的代码。

#include <iostream>
#include <map>
#include <functional>

void foo(int x){std::cout<<x;}
void bar(){std::cout<<"bar";}


std::map<std::string, std::function<void()>> map = 
{
    {"foo", foo},
    {"bar", bar}
};

int main()
{
    map["foo"](2);
    map["bar"]();
}

我不反对C风格的解决方案。

4 个答案:

答案 0 :(得分:1)

您可以将指针声明为旧样式C函数指针 一个可变函数,如:

 foo(...);
 bar(...);

 std::map<void(*)(...)> map = 
 {
    {"foo", foo},
    {"bar", bar}
 };

但是foo和bar必须遵循va_args,va_start的可变参数调用约定  等等,您只能从列表中提取C POD。不知道它是否值得麻烦。调用方法仍然必须知道要传递多少个args。

看起来有点像你可能会重新考虑你的设计。

例如,如果这应该是一种CLI的命令表,那么最好将std::vector<std::string>传递给每个潜在的命令并让它弄清楚向量是否具有正确的大小()它的目的。

答案 1 :(得分:1)

如果您完全放弃了类型系统,只要您在各处获得所有类型,就可以使用boost::any。现在只适用于明确地将所有内容都设为std :: function,但我确定还有一个解决方法(更新为自由函数添加了一个重载):

class Functions
{
public:
    template <typename... T>
    void add_function(const std::string& name, void (*f)(T...))
    {   
        fs[name] = std::function<void(T...)>{f};
    }   


    template <typename... T>
    void add_function(const std::string& name, std::function<void(T...)> f)
    {   
        fs[name] = f;
    }   

    template <typename... T>
    void call(const std::string& name, T... args)
    {   
        auto it = fs.find(name);
        if (it != fs.end()) {
            auto f = boost::any_cast<std::function<void(T...)>>(&it->second);
            if (f) {
                (*f)(args...);
            }   
            else {
                std::cout << "invalid args for " << name << std::endl;
            }
        }   
        else {
            std::cout << "not found: " << name << std::endl;
        }   
    }   

private:
    std::map<std::string, boost::any> fs; 
};

void baz() {
    std::cout << "baz" << std::endl;
}

int main() {
    std::function<void()> foo = []{ std::cout << "foo" << std::endl; };
    std::function<void(int)> bar = [](int i){ std::cout << "bar(" << i << ")" << std::endl;     
};

    Functions f;
    f.add_function("foo", foo );
    f.add_function("bar", bar);
    f.add_function("baz", baz);

    f.call("foo");
    f.call("bar", 42);
    f.call("baz");
}

功能性,是的。好主意?另请注意,f.call("bar", 42u)将失败,因为您必须使每个类型完全正确

答案 2 :(得分:0)

我改变了你的方法,并且意识到它只是一个例子,我很确定它不会像这样编译,但它会让你知道我的想法。 您可以在其他结构中注册函数,然后调用相应的函数,转发参数。

struct Funcs
{
    std::function<void(int)> _f1;
    std::function<void()>    _f2;

    template<typename args...>
    void call(std::string&& f_name, args...)
    {
        if(f_name == "foo")
          _f1(std::forward(args)...)
        if(f_name == "bar")
          _f2(std::forward(args)...)
    }
}


int main()
{
    Funcs f;
    f.call("foo", 2);
}

答案 3 :(得分:0)

如果你真的想存储任何函数,并且总能弄清楚如何正确调用它,你可以扩展Oncaphillis的方法,然后继续编译函数指针:

void foo(int);
float bar(double, struct baz);

std::map<void(*)()> map = {
    {"foo", (void(*)())foo},
    {"bar", (void(*)())bar}
};

然后你可以在使用它们时将它们抛回:

//code to make sure that map["foo"] is of type `void(*)(int)`
(*(void(*)(int))map["foo"])(42);

//code to make sure that map["bar"] is of type `float(*)(double, struct baz)`
float result = (*(float(*)(double, struct baz))map["foo"])(3.14159, (struct baz){ /*whatever*/});

如您所见,以这种方式调用任何类型的函数都没有问题,而不限制可变参数。但是,这种方法非常容易出错,因为您完全取消了类型系统提供的安全性,并且您的演员表必须100%正确。如果你不这样做,可能会发生奇怪的事情。这与使用boost::any时的问题相同。