【问题标题】:map of pointers to functions of different return types and signatures指向不同返回类型和签名的函数的指针映射
【发布时间】:2012-10-09 00:06:46
【问题描述】:

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

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

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

用法是:

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

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

【问题讨论】:

  • 如何使函数的返回类型为函数的“序列化结果”(例如字符向量)以使函数信号相同?
  • 是的,这是一种选择。在考虑这种方法时,我遇到了独立整数(肯定会发送)的问题,但我猜这是另一个问题。
  • 希望我能帮助你,但我不明白独立整数的问题是什么。 :D

标签: c++ boost c++11 boost-asio function-pointers


【解决方案1】:

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

【讨论】:

    【解决方案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;
    }
    

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

    【讨论】:

    • 如何查找这样的东西?
    • @jt234 你不必这样做。地图会为您处理。
    • 我将如何编写一个接收字符串并执行函数的函数?您的示例包含大量硬编码信息。我的目标是:runFuncByName(string cmd)
    • 是的,我明白那里发生了什么。我的问题是重新解释演员阵容仍然保持不变。如果我只需要一个功能,程序就不会知道需要哪一个,不是吗?呼叫将完全来自另一台机器。
    • 您将需要更多的 if() 条件来知道要执行哪个条件。可悲的是,当涉及到“类型”时,C++ 过于“严格”。唯一的办法是使用这样的黑客/技巧。
    【解决方案3】:

    您可以存储负责桥接不匹配的适配器,而不是存储函数指针本身,因为它们彼此差异太大而无法容纳到相同的数据结构中。这是类型擦除的一种形式。一个例子:

    // 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&amp;) 用作callback_type

    请注意,这种设计有点脆弱,因为context_type 需要了解存储的回调可能需要的每一种 类型的参数。如果稍后您需要存储需要一种新参数的回调,则需要修改context_type——如果您改进上述设计以使用像0这样的幻数和1 作为std::get 的参数,您可以省去一些麻烦(尤其是在从context_type 中删除类型的相反情况下)。如果所有回调都采用相同的参数,这不是问题,在这种情况下,您可以完全省去 context_type 并将这些参数直接传递给回调。

    Demonstration on LWS.

    【讨论】:

    • 这绝不是唯一的方法。这个答案不是确定的,建议看看其他答案。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

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

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

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

          • 好的,我可以选择不同的返回类型。据我所知,返回类型不是非模板函数签名的一部分。只有这样可能吗?
          • 我有办法解决这个问题。我稍后会发帖
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-06
          • 2013-04-10
          相关资源
          最近更新 更多