2010-01-26 8 views
31

मैंने एक स्क्रिप्टिंग इंजन विकसित किया जिसमें कई अंतर्निहित फ़ंक्शंस हैं, इसलिए किसी भी फ़ंक्शन को कॉल करने के लिए, मेरा कोड सिर्फ if .. else if .. else if दीवार की जांच कर रहा है लेकिन मैं एक और अधिक कुशल समाधान विकसित करना चाहता हूं ।फ़ंक्शन पॉइंटर्स के एक एसटीएल मानचित्र का उपयोग

क्या मुझे हैशप का उपयोग स्ट्रिंग्स के साथ कुंजी और पॉइंटर्स के रूप में करना चाहिए? एसटीएल मानचित्र का उपयोग करके मैं इसे कैसे कर सकता हूं?

संपादित: एक और मुद्दा यह है कि मेरे दिमाग में आया: एक मानचित्र का उपयोग संकलक कार्यों इनलाइन के लिए नहीं के लिए बाध्य करेगा, लेकिन मेरे अक्षम दृष्टिकोण किसी भी भूमि के ऊपर समारोह कॉल की आवश्यकता द्वारा उत्पन्न की है नहीं था जाहिर है, यह बस कोड निष्पादित करता है।

तो मुझे आश्चर्य है कि फ़ंक्शन कॉल द्वारा उत्पन्न ओवरहेड if..else श्रृंखला होने से बेहतर होगा .. अन्यथा मैं रनटाइम पर एक वर्ण की जांच करके तुलना की संख्या को कम कर सकता हूं (लंबे समय तक तेज़ होगा)।

उत्तर

36

जो भी आपकी समारोह हस्ताक्षर कर रहे हैं:

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*/> का सामान्यीकरण किया जा सकता है तो आप किसी भी प्रतिदेय बात, बस वास्तव में समारोह संकेत नहीं समर्थन कर सकते हैं।

+1

इसके अलावा वास्तव में 'unordered_map' जैसी असली हैश तालिका का उपयोग करने की आवश्यकता नहीं है। ऐसा नहीं होगा कि हैश टेबल कई तत्व प्रदर्शन प्रदर्शन लाएगा, अगर इस मामले में 'मैप' तेज था तो भी मुझे आश्चर्य नहीं होगा। – sth

+3

दरअसल, मैंने कुछ समान सामान किए हैं और 'unordered_map' * बहुत * तेज था। मेरे पास केवल 10,000 चीजें थीं, और मैंने 'नक्शा' और 'unordered_map' दोनों का प्रोफाइल किया। – GManNickG

+1

मैं अपेक्षा करता हूं कि "कई बिल्टिन फ़ंक्शंस" << 10.000'। ओपी के मामले में हैसमप के पास "सच्चा ओ (1)" होने का स्पष्ट लाभ है क्योंकि इसे बढ़ने की ज़रूरत नहीं है, और तारों के लिए टकराव मुक्त हैश का निर्माण किया जा सकता है। मुझे संदेह है कि यह कुछ 100 वस्तुओं के लिए 'मानचित्र' की तुलना में * महत्वपूर्ण * अंतर बनाता है। – peterchen

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; 
} 
+1

दरअसल, मुझे यह बहुत उपयोगी लगता है। आप मूल रूप से अपने स्वयं के फ़ंक्शंस लिख सकते हैं जो reinterpreting (यानी फ्लोट my_b() {वापसी reinterpret .....}। –

+3

क्या आपने वास्तव में सी ++ प्रोग्राम में 'शून्य मुख्य' लिखा था? –

+3

@ एलबी--: क्यों क्या आप इसे संपादित नहीं करते हैं? ओह रुको .. 140 प्रतिनिधि – Jacob

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

यह रूप में संगत प्रोटोटाइप के कार्यों में से एक नक्शे के किया जा सकता है ठीक है, बिल्कुल।

+0

यह मेरे लिए काम नहीं करता। मुझे एक कंपाइलर त्रुटि मिली। 'boost :: function': बहुत सारे टेम्पलेट तर्क – excray

+0

@ विवेक-जी, कई समस्याएं संभव हैं , कंपाइलर संस्करण, गायब शामिल है, आदि। यह मेरे लिए संकलित और कोडपैड के लिए चलाता है: http://codepad.org/ciKTrh2r – mloskot

+1

@mloskot, शानदार उदाहरण के लिए धन्यवाद! –

7

से ऊपर जवाब इसकी पूरी जानकारी देने के लिए लगता है, यह केवल आपके दूसरे प्रश्न का संबंध: कुंजी द्वारा

मानचित्र तत्व पुनर्प्राप्ति हे (लॉग एन) जटिलता है। कुंजी द्वारा हैशमैप पुनर्प्राप्ति ओ (1) जटिलता + टकराव के मामले में तरफ एक छोटी सी चीज है। तो यदि आपके फ़ंक्शन नामों के लिए एक अच्छा हैश फ़ंक्शन है, तो इसका उपयोग करें। आपके कार्यान्वयन में एक मानक होगा। यह अच्छा होना चाहिए।

लेकिन ध्यान रखें कि सौ तत्वों से कम कुछ भी बहुत लाभ नहीं उठाएगा।

हैश मानचित्र का एकमात्र नकारात्मक टक्कर टक्कर है। आपके मामले में, हैशपैप अपेक्षाकृत स्थिर होगा। आप जिन फ़ंक्शन नामों का समर्थन करते हैं उन्हें आप जानते हैं। इसलिए मैं आपको एक साधारण परीक्षण केस बनाने की सलाह देता हूं, जहां आप unordered_map < ...> :: हैश_फंक्शन को अपनी सभी चाबियों के साथ कॉल करने के लिए यह सुनिश्चित करने के लिए कहते हैं कि कुछ भी टकरा नहीं जाता है। उसके बाद, आप इसके बारे में भूल सकते हैं।

हैश फंक्शन पर संभावित सुधार के लिए एक त्वरित गूगल मुझे वहाँ गया:

A fiew good hash functions

हो सकता है, आपके नामकरण सम्मेलनों के आधार पर आप समारोह के कुछ पहलुओं पर सुधार कर सकते हैं।

0

मैंने सी ++ 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

सी ++ 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; 
} 
+0

यह सोना है। क्या यह संभव है मिश्रण में सदस्य फ़ंक्शंस जोड़ें? शायद इसे किसी गैर-सदस्य प्रकार में किसी बिंदु पर कास्टिंग करके? धन्यवाद – LRP