【问题标题】:Using a STL map of function pointers使用函数指针的 STL 映射
【发布时间】:2011-01-09 08:51:38
【问题描述】:

我开发了一个具有许多内置函数的脚本引擎,因此要调用任何函数,我的代码只是进入if .. else if .. else if 墙检查名称,但我想开发一个更有效的解决方案。

我应该使用 hashmap,其中字符串作为键,指针作为值吗?我怎么能通过使用 STL 映射来做到这一点?

编辑: 我想到的另一点:当然使用映射会强制编译器不要内联函数,但我的低效方法没有因函数调用的必要性而产生任何开销,它只是执行代码。

所以我想知道函数调用产生的开销是否会比拥有if..else 链更好。否则我可以通过在运行时检查一个字符来最小化比较次数(会更长但更快)。

【问题讨论】:

    标签: c++ stl map function-pointers


    【解决方案1】:

    我尝试将示例修改为接口作为类的成员并包装对 searchAndCall 的调用,但是函数 fun1 堆栈内部已损坏,a 和 b 变量错误 实际上它在断言assert(mapVal.second == std::type_index(typeid(typeCastedFun))); 上失败了,因为不同的类型 写包装器有多正确?

    template <typename A>
    using voidFunctionType = void (A::*)(void);
    
    template <typename A>
    struct Interface {
    
        std::map<std::string, std::pair<voidFunctionType<A>, std::type_index>> m1;
    
        template<typename T>
        void insert(std::string s1, T f1) {
            auto tt = std::type_index(typeid(f1));
            m1.insert(std::make_pair(s1,
                std::make_pair((voidFunctionType<A>)f1, tt)));
        }
    
        template<typename T, typename... Args>
        T searchAndCall(A* a, std::string s1, Args&&... args) {
            auto mapIter = m1.find(s1);
            auto mapVal = mapIter->second;
    
            auto typeCastedFun = (T(A::*)(Args ...))(mapVal.first);
            auto type = std::type_index(typeid(typeCastedFun));
            assert(mapVal.second == std::type_index(typeid(typeCastedFun)));
            return (a->*typeCastedFun)(std::forward<Args>(args)...);
        }
    };
    class someclass2
    {
        Interface<someclass2> a1;
    public:
        someclass2()
        {
            a1.insert("fun1", &someclass2::fun1);
        }
        int fun1(int a, int b)
        {
            return a + b;
        }
        void masterfunction(int a, int b)
        {
            int result =  a1.searchAndCall<int>(this, "fun1", a,b);
            std::cout << "Result " << result << std::endl;
        }
    };
    int main()
    {
        someclass2 s1;
        s1.masterfunction(1, 2);
        return 0;
    }
    

    【讨论】:

      【解决方案2】:

      我已经设法修改 example from Mohit 以处理成员函数指针:

      #include <string>
      #include <iostream>
      #include <map>
      #include <vector>
      #include <typeinfo>
      #include <typeindex>
      #include <cassert>
      
      
      template <typename A>
      using voidFunctionType = void (A::*)(void);
      
      template <typename A>
      struct Interface{
      
          std::map<std::string,std::pair<voidFunctionType<A>,std::type_index>> m1;
      
          template<typename T>
          void insert(std::string s1, T f1){
              auto tt = std::type_index(typeid(f1));
              m1.insert(std::make_pair(s1,
                              std::make_pair((voidFunctionType<A>)f1,tt)));
          }
      
          template<typename T,typename... Args>
          T searchAndCall(A a, std::string s1, Args&&... args){
              auto mapIter = m1.find(s1);
              auto mapVal = mapIter->second;  
      
              auto typeCastedFun = (T(A::*)(Args ...))(mapVal.first); 
      
              assert(mapVal.second == std::type_index(typeid(typeCastedFun)));
              return (a.*typeCastedFun)(std::forward<Args>(args)...);
          }
      };
      
      class someclass {
          public:
              void fun1(void);
              int fun2();
              int fun3(int a);
              std::vector<int> fun4();
      };
      
      void someclass::fun1(void){
          std::cout<<"inside fun1\n";
      }
      
      int someclass::fun2(){
          std::cout<<"inside fun2\n";
          return 2;
      }
      
      int someclass::fun3(int a){
          std::cout<<"inside fun3\n";
          return a;
      }
      
      std::vector<int> someclass::fun4(){
          std::cout<<"inside fun4\n";
          std::vector<int> v(4,100);
          return v;
      }
      
      int main(){
          Interface<someclass> a1;
          a1.insert("fun3",&someclass::fun3);
           someclass s;
          int retVal = a1.searchAndCall<int>(s, "fun3", 3);
          return 0;
      }
      

      【讨论】:

        【解决方案3】:

        好吧,您可以使用any_map 来存储具有不同签名的函数(但调用它会很麻烦),您可以使用int_map 来调用具有特定签名的函数(看起来更好)。

        int FuncA()
        {
            return 1;
        }
        
        float FuncB()
        {
            return 2;
        }
        
        
        int main()
        {
            // Int map
            map<string,int(*)()> int_map;
            int_map["A"] = FuncA;
            // Call it
            cout<<int_map["A"]()<<endl;
        
            // Add it to your map
            map<string, void(*)> any_map;
            any_map["A"] = FuncA;
            any_map["B"] = FuncB;
        
            // Call
            cout<<reinterpret_cast<float(*)()>(any_map["B"])()<<endl;
        }
        

        【讨论】:

        • 其实我觉得这很有用。您基本上可以编写自己的函数来完成重新解释(即 float my_b(){ return reinterpret.....}。
        • 你真的只是在 C++ 程序中写了void main 吗?
        【解决方案4】:

        无论你的函数签名是什么:

        typedef void (*ScriptFunction)(void); // function pointer type
        typedef std::unordered_map<std::string, ScriptFunction> script_map;
        
        // ...
        
        void some_function()
        {
        }
        
        // ...
        
        script_map m;
        m.emplace("blah", &some_function);
        
        // ...
        
        void call_script(const std::string& pFunction)
        {
            auto iter = m.find(pFunction);
            if (iter == m.end())
            {
                // not found
            }
        
            (*iter->second)();
        }
        

        请注意,ScriptFunction 类型可以泛化为 std::function&lt;/* whatever*/&gt;,因此您可以支持任何可调用的东西,而不仅仅是函数指针。

        【讨论】:

        • 另外真的没有必要使用像unordered_map这样的真正的哈希表。哈希表不会带来那么多元素带来性能优势,如果map 在这种情况下更快,我什至不会感到惊讶。
        • 实际上,我做过一些类似的事情,unordered_map 的速度快得多。我里面只有大约 10,000 件东西,我分析了 mapunordered_map
        • 我希望"many builtin functions" &lt;&lt; 10.000。在 OP 的情况下,Hasmap 具有明显的优势,即“真正的 O(1)”,因为它不必增长,并且可以为字符串构造无冲突的哈希。我怀疑它与 map 相比,即使是 100 件商品也有显着的差异。
        • 实际上“许多内置函数”就像〜100。当然,它们可以随着时间的推移而增长,但毫无疑问,它们会达到 1000 个。我会尝试使用地图。也因为到目前为止我没有使用 Boost,我会避免这种情况(只是因为我真的完成了除了一些优化之外的所有事情)。
        • 这真的有效吗?您是否还需要从地图中检索函数指针?即倒数第二行应该是 (*(iter->second))();什么的。
        【解决方案5】:

        在 C++11 中,您可以执行以下操作: 这个接口只需要返回类型,它会处理调用方的所有其他事情。

        #include <string>
        #include <iostream>
        #include <map>
        #include <vector>
        #include <typeinfo>
        #include <typeindex>
        #include <cassert>
        
        void fun1(void){
            std::cout<<"inside fun1\n";
        }
        
        int fun2(){
            std::cout<<"inside fun2\n";
            return 2;
        }
        
        int fun3(int a){
            std::cout<<"inside fun3\n";
            return a;
        }
        
        std::vector<int> fun4(){
            std::cout<<"inside fun4\n";
            std::vector<int> v(4,100);
            return v;
        }
        
        // every function pointer will be stored as this type
        typedef void (*voidFunctionType)(void); 
        
        struct Interface{
        
            std::map<std::string,std::pair<voidFunctionType,std::type_index>> m1;
        
            template<typename T>
            void insert(std::string s1, T f1){
                auto tt = std::type_index(typeid(f1));
                m1.insert(std::make_pair(s1,
                                std::make_pair((voidFunctionType)f1,tt)));
            }
        
            template<typename T,typename... Args>
            T searchAndCall(std::string s1, Args&&... args){
                auto mapIter = m1.find(s1);
                /*chk if not end*/
                auto mapVal = mapIter->second;
        
                // auto typeCastedFun = reinterpret_cast<T(*)(Args ...)>(mapVal.first); 
                auto typeCastedFun = (T(*)(Args ...))(mapVal.first); 
        
                //compare the types is equal or not
                assert(mapVal.second == std::type_index(typeid(typeCastedFun)));
                return typeCastedFun(std::forward<Args>(args)...);
            }
        };
        
        int main(){
            Interface a1;
            a1.insert("fun1",fun1);
            a1.insert("fun2",fun2);
            a1.insert("fun3",fun3);
            a1.insert("fun4",fun4);
        
            a1.searchAndCall<void>("fun1");
            int retVal = a1.searchAndCall<int>("fun3",2);
            a1.searchAndCall<int>("fun2");
            auto temp = a1.searchAndCall<std::vector<int>>("fun4");
        
            return 0;
        }
        

        【讨论】:

        • 这是黄金。是否可以将成员函数添加到组合中?也许通过在某个时候将其转换为非成员类型?谢谢
        • 如果调用是指向成员函数的指针,它的外观如何?我想做同样的事情。如果我有这个:typedef int(ObjectT::*Command)(); 并且调用会引发错误。 int result = (*itr-&gt;second)(); 在指向成员的指针上无效地使用一元 *。
        【解决方案6】:

        我尝试在 c++11 中使用第二个答案。我不得不改变最后一行 来自:
        (*iter)();
        到:
        (*iter->second)();

        所以现在的代码是:

            #include <map>
        
            typedef void (*ScriptFunction)(void); // function pointer type
            typedef std::map<std::string, ScriptFunction> script_map;
        
            // ...
        
            void some_function(void)
            {
            }
            script_map m;
        
            void call_script(const std::string& pFunction)
            {
                script_map::const_iterator iter = m.find(pFunction);
                if (iter == m.end())
                {
                    // not found
                }
                (*iter->second)();
            }
        
            int main(int argc, const char * argv[])
            {
                //..
                m.insert(std::make_pair("blah", &some_function));
        
                call_script("blah");
                //..
                return 0;
            }
        

        【讨论】:

          【解决方案7】:

          以上答案似乎给出了一个完整的概述,这仅涉及您的第二个问题:

          按键检索地图元素具有 O(log n) 复杂度。通过键检索哈希图具有 O(1) 复杂性 + 在发生冲突时会附带一些东西。因此,如果您的函数名称有一个好的散列函数,请使用它。您的实施将有一个标准的实施。应该没问题的。

          但请注意,任何低于一百个元素的东西都不会受益太多。

          哈希映射的唯一缺点是冲突。在您的情况下,哈希图将是相对静态的。您知道您支持的函数名称。因此,我建议您创建一个简单的测试用例,在其中使用所有键调用 unordered_map<...>::hash_function 以确保没有碰撞。之后,你就可以忘记它了。

          一个关于哈希函数潜在改进的快速谷歌让我到了那里:

          A fiew good hash functions

          也许,根据您的命名约定,您可以改进该功能的某些方面。

          【讨论】:

            【解决方案8】:

            您还可以使用Boost.FunctionBoost.Bind,它们甚至可以在某种程度上让您拥有异构函数的映射:

            typedef boost::function<void, void> fun_t;
            typedef std::map<std::string, fun_t> funs_t;
            funs_t f;
            
            void foo() {}
            void goo(std::string& p) {}
            void bar(int& p) {}
            
            f["foo"] = foo;
            f["goo"] = boost::bind(goo, "I am goo");
            f["bar"] = boost::bind(bar, int(17));
            

            当然,它也可以是兼容原型的功能映射。

            【讨论】:

            • 这对我不起作用。我得到一个编译器错误。 'boost::function' : 模板参数太多
            • @vivek-g,可能存在很多问题,编译器版本,缺少包含等。它确实为我以及键盘编译和运行:codepad.org/ciKTrh2r
            猜你喜欢
            • 1970-01-01
            • 2012-08-04
            • 1970-01-01
            • 2017-08-08
            • 1970-01-01
            • 1970-01-01
            • 2014-09-27
            • 1970-01-01
            • 2017-11-24
            相关资源
            最近更新 更多