我正在寻找一种通过字符串输入调用不同函数的方法。
我有一个映射,它将每个唯一字符串与一个函数指针和一个查找函数联系起来,以搜索映射并在找到时返回一个指针。
现在的诀窍是,我需要一种方法来存储和返回指向函数的指针,这些函数至少具有不同的返回类型,如果可能的话,还具有不同的签名。
用法是:
从网络套接字获取字符串输入 -> 查找并执行找到的函数 -> 将结果直接推回套接字以进行序列化和发送,而不关心实际发生了什么。
这是可行的吗?如果没有,人们将如何处理这项任务?
我正在寻找一种通过字符串输入调用不同函数的方法。
我有一个映射,它将每个唯一字符串与一个函数指针和一个查找函数联系起来,以搜索映射并在找到时返回一个指针。
现在的诀窍是,我需要一种方法来存储和返回指向函数的指针,这些函数至少具有不同的返回类型,如果可能的话,还具有不同的签名。
用法是:
从网络套接字获取字符串输入 -> 查找并执行找到的函数 -> 将结果直接推回套接字以进行序列化和发送,而不关心实际发生了什么。
这是可行的吗?如果没有,人们将如何处理这项任务?
这可以通过一些样板代码以不同的方式完成。如果签名的数量足够小,您可以保存多个函数指针向量(每个函数类型一个),然后是一个映射,该映射将函数名称与类型标识符(用于选择向量)和向量中的位置进行映射。
第二个选项是存储一个boost::variant
(同样,如果签名集很小)。您需要提供一个访问者对象来评估函数(针对存储的每个函数类型)并产生结果。类型由类型管理,boost::variant
因此不需要将类型标签存储在地图中。
您还可以使用完全类型擦除并将确定要调用的函数类型的标记和boost::any
存储函数指针的对象存储在映射中。您可以使用类型信息来检索指针并执行函数,但您必须手动处理switch
基于函数类型的函数。
另一方面,最简单的方法是编写具有固定接口的适配器。然后只需将指向适配器的指针存储在映射中。
虽然您不能存储不同的函数指针,但您可以存储包含这些函数的对象。
#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;
}
我认为编辑的部分使它更清晰?这段代码可以稍微优化一下。玩弄它。
这在带有可变参数模板的 C++11 中是可行的。在https://stackoverflow.com/a/33837343/1496826检查我的答案
不,这真的不可行,如果你想做这样的事情,你需要一种真正的解释语言。一旦签名不是恒定的,那么你需要更多的东西。
您可以存储负责桥接不匹配的适配器,而不是存储函数指针本身,因为它们彼此差异太大而无法容纳到相同的数据结构中。这是类型擦除的一种形式。一个例子:
// 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
——如果您改进上述设计,不使用像0
和1
作为参数这样的幻数,std::get
您可以省去一些麻烦(尤其是在从context_type
) 中删除类型的相反情况。如果所有回调都采用相同的参数,这不是问题,在这种情况下,您可以context_type
完全放弃自己并将这些参数直接传递给回调。
如何让所有这些函数具有相同的签名?您可以使所有返回类型都实现一个接口,或者使用集合、类、联合或结构。论据也一样。
你不能使用专业化和模板来解决这个问题吗?
template <class T>
T FooBar(void * params);
template<> int FooBar<int>( void * params );
template<> char FooBar<char>( void * params );