4

我正在寻找一种通过字符串输入调用不同函数的方法。

我有一个映射,它将每个唯一字符串与一个函数指针和一个查找函数联系起来,以搜索映射并在找到时返回一个指针。

现在的诀窍是,我需要一种方法来存储和返回指向函数的指针,这些函数至少具有不同的返回类型,如果可能的话,还具有不同的签名。

用法是:

从网络套接字获取字符串输入 -> 查找并执行找到的函数 -> 将结果直接推回套接字以进行序列化和发送,而不关心实际发生了什么。

这是可行的吗?如果没有,人们将如何处理这项任务?

4

7 回答 7

4

这可以通过一些样板代码以不同的方式完成。如果签名的数量足够小,您可以保存多个函数指针向量(每个函数类型一个),然后是一个映射,该映射将函数名称与类型标识符(用于选择向量)和向量中的位置进行映射。

第二个选项是存储一个boost::variant(同样,如果签名集很小)。您需要提供一个访问者对象来评估函数(针对存储的每个函数类型)并产生结果。类型由类型管理,boost::variant因此不需要将类型标签存储在地图中。

您还可以使用完全类型擦除并将确定要调用的函数类型的标记和boost::any存储函数指针的对象存储在映射中。您可以使用类型信息来检索指针并执行函数,但您必须手动处理switch基于函数类型的函数。

另一方面,最简单的方法是编写具有固定接口的适配器。然后只需将指向适配器的指针存储在映射中。

于 2012-10-18T14:06:18.543 回答
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;
}

我认为编辑的部分使它更清晰?这段代码可以稍微优化一下。玩弄它。

于 2012-10-18T14:45:09.247 回答
1

这在带有可变参数模板的 C++11 中是可行的。在https://stackoverflow.com/a/33837343/1496826检查我的答案

于 2015-11-20T22:55:12.580 回答
0

不,这真的不可行,如果你想做这样的事情,你需要一种真正的解释语言。一旦签名不是恒定的,那么你需要更多的东西。

于 2012-10-18T13:50:21.180 回答
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——如果您改进上述设计,使用像01作为参数这样的幻数,std::get您可以省去一些麻烦(尤其是在从context_type) 中删除类型的相反情况。如果所有回调都采用相同的参数,这不是问题,在这种情况下,您可以context_type完全放弃自己并将这些参数直接传递给回调。

LWS 上的演示

于 2012-10-18T14:33:46.737 回答
0

如何让所有这些函数具有相同的签名?您可以使所有返回类型都实现一个接口,或者使用集合、类、联合或结构。论据也一样。

于 2012-10-18T14:00:40.223 回答
0

你不能使用专业化和模板来解决这个问题吗?

template <class T>
T FooBar(void * params);

template<> int FooBar<int>( void * params ); 

template<> char FooBar<char>( void * params ); 
于 2012-10-18T14:13:06.897 回答