我正在寻找一种通过字符串输入调用不同函数的方法。
我有一个地图,它将每个唯一的字符串绑定到一个函数指针和一个查找函数来搜索地图并返回一个指针(如果找到)。
现在的诀窍是,我需要一种方法来存储和返回指向至少具有不同返回类型的函数的指针,如果可能的话,还有不同的签名。
用法是:
从网络套接字获取字符串输入 - > 找到并执行找到的函数 - >将结果直接推回到要被序列化和发送的套接字中,而不是关心实际发生的事情。
这可行吗?如果没有,那么如何处理这项任务?
答案 0 :(得分:4)
可以通过不同的方式使用一些样板代码来完成。如果签名的数量足够小,您可以保存多个函数指针向量(每个函数类型一个),然后是一个映射函数名称的映射,其中包含类型标识符(用于选择向量)和向量中的位置。 / p>
第二个选项是存储boost::variant
(同样,如果签名集很小)。您需要提供一个访问者对象来评估函数(对于存储的每个函数类型)并生成结果。该类型由boost::variant
类型管理,因此不需要将类型标记存储在地图中。
您还可以使用完整类型擦除并在地图中存储确定要调用的函数类型的标记以及存储函数指针的boost::any
对象。您可以使用类型信息来检索指针并执行该函数,但您必须根据函数类型手动处理switch
。
另一方面,最简单的方法是编写具有固定接口的适配器。然后只需将指针存储到地图中的适配器。
答案 1 :(得分:2)
虽然您无法存储不同的函数指针,但您可以存储包含这些函数的对象。
#include <iostream>
#include <cmath>
#include <map>
#include <string>
using namespace std;
class Functor{
public:
template<class T>
void operator()(T data){}
};
template<class T>
class BaseFunctor : public Functor{
public:
virtual void CallFunction(T data){ }
};
class FunctionPointer1 : public BaseFunctor<void *>{
public:
void doFunction1(){
cout << "Do Function 1"<<endl;
}
template<class T>
void CallFunction(T data){ doFunction1(); }
template<class T>
void operator()(T data){ this->CallFunction(data); }
};
class FunctionPointer2 : public BaseFunctor<int>{
public:
void doFunction2(int variable){ cout << "Do function 2 with integer variable" << variable <<endl; }
template<class T>
void CallFunction(T data) { doFunction2(data);}
template<class T>
void operator()(T data){ this->CallFunction(data); }
};
class FunctionPerformer{
private:
map<string,Functor> functions;
public:
FunctionPerformer(){
//init your map.
FunctionPointer1 function1;
FunctionPointer2 function2;
//-- follows
functions["Function1"] = function1;
functions["Functions2"] = function2;
//-- follows
}
Functor getFunctionFromString(string str){
return functions[str]
}
};
int main(int argc, char *argv[])
{
map<string,Functor> functions;
FunctionPerformer performer;
Functor func1, func2; // to hold return values from perfomer()
FunctionPointer1 *fn1; // to casting and execute the functions
FunctionPointer2 *fn2; // to casting and execute the functions
func1 = performer.getFunctionFromString("Function1");//get data
func2 = performer.getFunctionFromString("Function2");
//following two lines to cast the object and run the methods
fn1 = reinterpret_cast<FunctionPointer1 *>(&func1);
(*fn1)(NULL);
//following two lines to cast the object and run the methods
fn2 = reinterpret_cast<FunctionPointer2 *>(&func2);
(*fn2)(10);
system("Pause");
return 0;
}
我认为编辑的部分更清楚了吗? 这段代码可以稍微优化一下。玩弄它。
答案 2 :(得分:0)
不,这真的不可行,如果你想做这样的事,你需要一种真正的解释语言。一旦签名不稳定,那么你需要更多的参与。
答案 3 :(得分:0)
如何使所有这些功能具有相同的签名?您可以使所有返回类型实现接口,或使用集合,类,联合或结构。同样的论点。
答案 4 :(得分:0)
你不能使用专业化和模板来解决这个问题吗?
template <class T>
T FooBar(void * params);
template<> int FooBar<int>( void * params );
template<> char FooBar<char>( void * params );
答案 5 :(得分:0)
不是存储功能指针本身,它们彼此太不相同,无法容纳到相同的数据结构中,您可以存储用于弥合不匹配的适配器。这是一种类型擦除。一个例子:
// Imaginary important resources
blaz_type get_blaz();
qux_type get_qux();
// The functions we'd like to put in our map
int foo(blaz_type);
std::string bar(qux_type);
using context_type = std::tuple<blaz_type, qux_type>;
using callback_type = std::function<void(context_type, socket_type&)>;
using std::get;
std::map<std::string, callback_type> callbacks = {
{
"foo"
, [](context_type context, socket_type& out)
{ marshall(out, foo(get<0>(std::move(context)))); }
}
, {
"bar"
, [](context_type context, socket_type& out)
{ marshall(out, bar(get<1>(std::move(context)))); }
}
};
在此示例中,适配器不具有状态,因此您实际上可以将void (*)(context_type, socket_type&)
用作callback_type
。
请注意,这种设计有点脆弱,因为context_type
需要知道存储的回调可能需要的每种类型的参数。如果稍后您需要存储需要新类型参数的回调,则需要修改context_type
- 如果您改进上述设计而不是以使用像{一样的幻数{1}}和0
作为1
的参数,您可以省去一些痛苦(特别是在从std::get
删除类型的相反情况下)。如果所有回调都采用相同的参数,这不是问题,在这种情况下,您可以完全省略context_type
并将这些参数直接传递给回调。
答案 6 :(得分:0)
这在使用Variadic模板的C ++ 11中是可行的。在https://stackoverflow.com/a/33837343/1496826
上查看我的回答