代码之家  ›  专栏  ›  技术社区  ›  Jack

使用函数指针的stl映射

  •  33
  • Jack  · 技术社区  · 15 年前

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

    我应该使用 散列表 以字符串作为键,指针作为值?我怎么能用STL图来做呢?

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

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

    7 回复  |  直到 6 年前
        1
  •  39
  •   GManNickG    9 年前

    无论您的功能签名是什么:

    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</* whatever*/> 所以您可以支持任何可调用的东西,而不仅仅是函数指针。

        2
  •  15
  •   mloskot    15 年前

    您也可以使用 Boost.Function Boost.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));
    

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

        3
  •  12
  •   Mohit    9 年前

    在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;
    }
    
        4
  •  7
  •   AndreasT    15 年前

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

    按键检索地图元素具有O(log n)复杂性。通过键进行的哈希图检索具有O(1)复杂性,并且在发生冲突时附带了一些内容。所以,如果函数名有一个好的哈希函数,就使用它。您的实现将有一个标准的实现。应该没问题。

    但是要知道,任何低于100个元素的元素都不会有太大的好处。

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

    一个快速的谷歌搜索哈希函数的潜在改进让我有了这样的感觉:

    A fiew good hash functions

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

        5
  •  3
  •   Abhinav Gauniyal DarzVader    8 年前

    好吧,你可以用 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;
    }
    
        6
  •  0
  •   EckhardN    11 年前

    我试着用C++ 11来使用第二个答案。我得换最后一行 来自:
    (ITER)();
    到:
    (*iter->秒)();

    现在代码是:

        #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
  •  0
  •   Ia Rigby    6 年前

    我已经设法修改了 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;
    }