我想从一个表中调用所有类型的函数。 (考虑返回类型都是无效的)
为了说明我在说什么,这里有一些显然不起作用的代码。
#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风格的解决方案。
答案 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
时的问题相同。