简体   繁体   English

指向不同返回类型和签名的函数的指针映射

[英]map of pointers to functions of different return types and signatures

I am looking for a way to call different functions by a string input. 我正在寻找一种通过字符串输入来调用不同函数的方法。

I have a map that ties each unique string to a function pointer and a lookup function to search the map and return a pointer if found. 我有一个映射,该映射将每个唯一的字符串与一个函数指针联系在一起,并具有一个查找函数来搜索该映射并返回一个指针(如果找到)。

Now the trick is, I need a way to store and return pointers to functions with at least different return types, if possible, also with different signatures. 现在的诀窍是,我需要一种方法来存储和返回指向至少具有不同返回类型(如果可能)以及具有不同签名的函数的指针。

The usage would be: 用法是:

Get a string input from a network socket -> find and execute the found function -> shove the result straight back into the socket to be serialized and sent, not caring what actually happened. 从网络套接字获取字符串输入->查找并执行找到的功能->将结果直接推回套接字以进行序列化和发送,而不在乎实际发生的情况。

Is this doable? 这可行吗? If not, how would one approach this task? 如果没有,一个人将如何完成这项任务?

That can be done with a bit of boilerplate code in different ways. 可以用一些不同的样板代码来完成。 If the number of signatures is small enough you can hold multiple vectors of function pointers (one per function type) and then a map that maps the function name with a type identifier (used to select the vector) and the position within the vector. 如果签名的数量足够少,则可以容纳多个函数指针向量(每种函数类型一个),然后容纳一个映射,该映射将函数名称与类型标识符(用于选择向量)以及向量在向量中的位置进行映射。

The second option would be to store a boost::variant (again, if the set of signatures is small). 第二种选择是存储boost::variant (同样,如果签名集很小)。 You would need to provide a visitor object that evaluates the function (for each function type stored) and yields the result. 您将需要提供一个访问者对象,该对象可以评估功能(针对存储的每种功能类型)并产生结果。 The type is managed by the boost::variant type so there would be no need for the type tag to be stored in the map. 该类型由boost::variant类型管理,因此不需要将类型标签存储在映射中。

You can also use full type erasure and store in the map a tag determining the type of function to be called and a boost::any object storing the function pointer. 您也可以使用完全类型擦除并将标记确定要调用的函数的类型以及boost::any存储函数指针的对象存储在映射中。 You can use the type information to retrieve the pointer and execute the function, but you will have to manually handle the switch based on function type. 您可以使用类型信息来检索指针并执行功能,但是必须根据功能类型手动处理switch

The simplest approach, on the other hand, is to write adapters that have a fixed interface. 另一方面,最简单的方法是编写具有固定接口的适配器。 Then just store the pointers to the adapters in the map. 然后,只需将指向适配器的指针存储在映射中即可。

While you can't store different function pointers, you can store objects which contain those functions. 虽然不能存储不同的函数指针,但是可以存储包含这些函数的对象。

#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;
}

I think the edited part makes it clearer? 我认为编辑部分使内容更清晰吗? This code can be optimized a little. 该代码可以进行一些优化。 Play around with it. 玩吧。

No, it's really not doable, you need a real interpreted language if you want to do something like this. 不,这实际上是不可行的,如果您想执行此类操作,则需要一种真正的解释语言。 As soon as the signature is not constant then you need something a lot more involved. 一旦签名不是恒定的,那么您需要做更多的事情。

How about making all those functions have the same signature? 如何使所有这些功能具有相同的签名? You could make all return types implement an interface, or use a collection, class, union or struct. 您可以使所有返回类型都实现一个接口,或者使用集合,类,联合或结构。 Same for the arguments. 参数相同。

Can't you use specialization and templates to work around the issue? 您不能使用专业化和模板来解决此问题吗?

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

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

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

Instead of storing the function pointers themselves, which are too different from one another to be accommodated into the same data structure, you can store adaptors that take care of bridging the mismatch. 可以存储适配器以消除不匹配,而不是存储彼此之间太不同而无法容纳在同一数据结构中的函数指针本身。 This is a form of type-erasure. 这是一种类型擦除的形式。 An example: 一个例子:

// 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)))); }
    }
};

In this example the adaptors are not stateful so you can actually use void (*)(context_type, socket_type&) as the callback_type . 在此示例中,适配器不是有状态的,因此您实际上可以将void (*)(context_type, socket_type&)用作callback_type

Do note that this kind of design is a bit brittle in that the context_type needs to know about every kind of parameter a stored callback might ever need. 请注意,这种设计有点脆弱,因为context_type需要了解存储的回调可能需要的每种参数。 If at some later point you need to store a callback which needs a new kind of parameter, you need to modify context_type -- if you improve the above design not to use magic numbers like 0 and 1 as parameters to std::get you could save yourself some pains (especially in the reverse situation of removing types from context_type ). 如果稍后需要存储需要新类型参数的回调,则需要修改context_type -如果改进了上述设计, 不要使用像01这样的幻数作为std::get参数为自己省去一些麻烦(特别是在从context_type中删除类型的相反情况下)。 This is not an issue if all callbacks take the same parameters, in which case you can dispense yourself with the context_type altogether and pass those parameters to the callbacks directly. 如果所有回调都使用相同的参数,这不是问题,在这种情况下,您可以完全省去context_type并将这些参数直接传递给回调。

Demonstration on LWS . 关于LWS的示范

This is doable in C++11 with Variadic Templates. 这在带有可变参数模板的C ++ 11中是可行的。 Check my answer at https://stackoverflow.com/a/33837343/1496826 https://stackoverflow.com/a/33837343/1496826中查看我的答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM