2010-01-26 13 views
31

Ich entwickelte eine Skript-Engine, die viele integrierte Funktionen hat, so dass jeder Code in eine Wand, die den Namen, aber ich würde gerne eine effizientere Lösung entwickeln würde .Verwenden einer STL-Karte von Funktionszeigern

Sollte ich eine hashmap mit Strings als Schlüssel und Zeiger als Werte verwenden? Wie kann ich dies mithilfe einer STL-Map tun?

EDIT: Ein weiterer Punkt, in dem Sinn kam: natürlich eine Karte mit nicht den Compiler zwingen Funktionen inline, aber mein ineffizienter Ansatz nicht Overhead durch die Notwendigkeit der Funktionsaufrufe generiert haben hat, es führt nur Code aus.

So frage ich mich, ob der Overhead durch den Funktionsaufruf besser als eine if..else Kette sein würde .. sonst könnte ich die Anzahl der Vergleiche durch Überprüfung eines Zeichens zur Laufzeit (wird länger, aber schneller) minimieren.

Antwort

36

Was auch immer Ihre Funktion Unterschriften sind:

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

Beachten Sie, dass die ScriptFunction Typ std::function</* whatever*/> verallgemeinert werden könnten, so können Sie jede aufrufbare Sache unterstützen, nicht nur genau Funktionszeiger.

+1

Auch gibt es keine Notwendigkeit, eine echte Hash-Tabelle wie 'unordered_map' zu verwenden. Es wird nicht so viele Elemente geben, dass eine Hash-Tabelle Performance-Vorteile bringen würde. Ich wäre sogar nicht überrascht, wenn "map" in diesem Fall schneller wäre. – sth

+3

Eigentlich habe ich ein paar ähnliche Sachen gemacht und 'unordered_map' war * viel * schneller. Ich hatte nur ungefähr 10.000 Dinge und profilierte sowohl 'map' als auch' unordered_map'. – GManNickG

+1

Ich würde erwarten "viele eingebaute Funktionen" << 10.000. Hasmap im OP-Fall hat den klaren Vorteil, "wahres O (1)" zu sein, da es nicht wachsen muss und ein kollisionsfreier Hash für die Strings konstruiert werden könnte. Ich bezweifle, dass es einen * signifikanten * Unterschied gegenüber einer "Karte" für sogar ein paar 100 Elemente macht. – peterchen

3

Nun, Sie können any_map verwenden, um Funktionen mit verschiedenen Signaturen zu speichern (aber Aufruf es wird chaotisch) und Sie können int_map verwenden, um Funktionen mit einer bestimmten Signatur (sieht besser aus).

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

Eigentlich finde ich das sehr nützlich. Sie können grundsätzlich Ihre eigenen Funktionen schreiben, die die Reinterpretation einschließen (zB float my_b() {return reinterpret .....}. –

+3

Haben Sie wirklich nur 'void main' in einem C++ Programm geschrieben? –

+3

@ LB--: Why bearbeite es nicht? Oh, warte .. 140 rep. – Jacob

15

Sie auch Boost.Function und Boost.Bind verwenden können, was Ihnen auch erlaubt, bis zu einem gewissen Grad, haben Karte von heterogenen Funktionen:

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

Es kann eine Karte von Funktionen kompatibler Prototypen sein als aber natürlich.

+0

Das funktionierte nicht für mich. Ich habe einen Compilerfehler. 'boost :: function': zu viele Vorlagenargumente – excray

+0

@ Vivek-g, es sind viele Probleme möglich , Compiler-Version, fehlende Include, etc. Es kompiliert und läuft für mich sowie für Codepad: http://codepad.org/ciKTrh2r – mloskot

+1

@mloskot, vielen Dank für super Beispiel! –

7

Above Antworten scheinen einen vollständigen Überblick zu geben, dies in Bezug auf nur Ihre zweite Frage:

Map Element Abruf durch Schlüssel hat O (log n) Komplexität. Hashmap-Abruf nach Schlüssel hat O (1) Komplexität + ein wenig Zeug auf der Seite im Falle von Kollisionen. Wenn es also eine gute Hash-Funktion für Ihre Funktionsnamen gibt, verwenden Sie sie. Ihre Implementierung wird standardisiert sein. Es sollte in Ordnung sein.

Aber seien Sie sich bewusst, dass alles unter hundert Elementen nicht allzu viel nutzen wird.

Der einzige Nachteil einer Hash-Karte ist die Kollision. In Ihrem Fall wird die Hashmap relativ statisch sein. Sie kennen die von Ihnen unterstützten Funktionsnamen. Ich rate Ihnen daher, einen einfachen Testfall zu erstellen, in dem Sie unordered_map < ...> :: hash_function mit all Ihren Schlüsseln aufrufen, um sicherzustellen, dass nichts kollidiert. Danach kannst du es vergessen.

Eine schnelle Google für mögliche Verbesserungen auf Hash-Funktionen hat mich dort:

A fiew good hash functions

Vielleicht, auf Ihre Namenskonventionen abhängig, Sie auf einige Aspekte der Funktion zu verbessern.

0

Ich habe versucht, die zweite Antwort mit C++ 11 zu verwenden. Ich musste die letzte Zeile ändern von:
(* iter)();
zu:
(* iter-> Sekunde)(); jetzt

so ist der Code:

#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

In C++ 11 Sie so etwas tun kann: diese Schnittstelle nur den Rückgabetyp benötigt und es kümmert sich um alles andere von der Anruferseite.

#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

Dies ist Gold. Ist es möglich zu Fügen Sie Elementfunktionen zu der Mischung hinzu? Vielleicht, indem Sie es zu einem Nichtmitgliedstyp an irgendeinem Punkt werfen? Danke – LRP

Verwandte Themen