2009-03-10 4 views
8

Ich möchte String zu einer Instanz Member-Funktionen zuordnen, und speichern Sie jede Zuordnung in der Karte.Store Zeiger auf Member-Funktion in der Karte

Was ist die saubere Art, so etwas zu tun?

class MyClass 
{ 
    //........ 
    virtual double GetX(); 
    virtual double GetSomethingElse(); 
    virtual double GetT(); 
    virtual double GetRR(); 
    //........ 
}; 


class Processor 
{ 
private: 
     typedef double (MyClass::*MemFuncGetter)(); 
     static map<std::string, MemFuncGetter> descrToFuncMap; 

public: 
     static void Initialize(); 
     void Process(Myclass m, string); 
}; 

void Processor::Initialize() 
{ 

    descrToFuncMap["X"]=&MyClass::GetX; 
    descrToFuncMap["SomethingElse"]=&MyClass::GetSomethingElse; 
    descrToFuncMap["RR"]=&MyClass::GetRR; 
    descrToFuncMap["T"]=&MyClass::GetT; 
}; 
void Processor::Process(MyClass ms, const std::string& key) 
{ 
    map<std::string, Getter>::iterator found=descrToFuncMap.find(key); 
    if(found!=descrToFuncMap.end()) 
    { 
     MemFuncGetter memFunc=found->second; 
     double dResult=(ms).*memFunc();  
     std::cout<<"Command="<<key<<", and result="<<result<<std::end;  
     } 
} 

lassen Sie mich wissen, wenn Sie ein Problem mit diesem Ansatz sehen und was sind die gängigen Idiome dafür?

Vielleicht sollte ich if-else-if-Anweisung Kette, da ich eine begrenzte Anzahl von Elementfunktionen haben, statt einer verwirrenden Karte von func Zeiger

BTW, ich einige der nützlichen gefunden hier Informationen im c++-faq-lite

+0

Eigentlich denke ich, die Karte ist schöner als eine Wenn-sonst-Kette. Es gibt Ihnen einen schönen Haken für das Speichern anderer Meta-Informationen, wenn es nötig ist - erweitern Sie einfach den Werttyp vom Funktionszeiger zu einer Struktur, die einen Funktionszeiger enthält und welche anderen Informationen Sie auch brauchen. –

+0

Ich stimme zu, sieht besser aus, obwohl es für einen weniger erfahrenen Entwickler schwieriger zu verstehen ist. –

Antwort

6

Sieht gut aus für mich, aber für die Tatsache, dass descrToFuncMapstatic deklariert werden muss, wenn Sie beabsichtigen, es aus der statischen Funktion Initialize() zu initialisieren.

Wenn Sie sicherstellen möchten, dass Initialize() aufgerufen wird und nur einmal aufgerufen wird, können Sie das Singleton-Muster verwenden. Wenn Sie Multithreading nicht durchführen, bedeutet das im Grunde, dass Sie descrToFuncMap innerhalb einer eigenen Klasse (genannt say FuncMap) mit einem privaten Konstruktor umhüllen, der Initialize() aufruft. Dann fügen Sie eine static lokale Variable vom Typ FuncMap zu Processor::Process() hinzu - da die Variable static ist, bleibt sie bestehen und wird nur einmal initialisiert.

Beispiel-Code (ich weiß jetzt, dass friend nicht wirklich notwendig ist hier):

class Processor { 
private: 
    typedef double (MyClass::*MemFuncGetter)(); 

    class FuncMap { 
    public: 
     FuncMap() { 
      descrToFuncMap["X"]=&MyClass::GetX; 
      descrToFuncMap["SomethingElse"]=&MyClass::GetSomethingElse; 
      descrToFuncMap["RR"]=&MyClass::GetRR; 
      descrToFuncMap["T"]=&MyClass::GetT; 
     } 

     // Of course you could encapsulate this, but its hardly worth 
     // the bother since the whole class is private anyway. 
     map<std::string, MemFuncGetter> descrToFuncMap; 
    }; 

public: 
    void Process(Myclass m, string); 
}; 

void Processor::Process(MyClass ms, const std::string& key) { 
    static FuncMap fm;  // Only gets initialised on first call 
    map<std::string, Getter>::iterator found=fm.descrToFuncMap.find(key); 
    if(found!=fm.descrToFuncMap.end()) { 
     MemFuncGetter memFunc=found->second; 
     double dResult=(ms).*memFunc();  
     std::cout<<"Command="<<key<<", and result="<<result<<std::end;  
    } 
} 

Dies ist nicht die „wahren“ Singleton Muster wie verschiedene Funktionen könnten ihre eigenen, separaten Instanzen von FuncMap schaffen, aber es ist genug für das, was Sie brauchen. Für "echtes" Singleton würden Sie FuncMaps Konstruktor private deklarieren und eine statische Methode hinzufügen, sagen getInstance(), die die einzige Instanz als eine static Variable definierte und einen Verweis darauf zurückgab. Processor::Process() würde dies dann verwenden, um mit

FuncMap& fm = FuncMap::getInstance(); 
+0

ja es ist statisch (behoben). Ich mag die Idee einer privaten Freundesklasse. Würdest du es mit einem Beispiel demonstrieren? Danke –

0

würde ich

void Processor::Process(MyClass ms, std::string key) 

zu

void Processor::Process(const MyClass& ms, const std::string& key) 
01 ändern

Keine schlechte Nebenwirkung für jetzt sehen. Wahrscheinlich mit boost :: Funktion als Kartenwert wird es in Zukunft einfacher sein.

+0

nun ja, offensichtlich würde ich string als const zu referenzieren. Meine Frage ist vielleicht, ich sollte If-Statement-Chain verwenden, da ich eine begrenzte Anzahl von Member-Funktionen anstelle einer verwirrenden Karte von Func Pointer –

+0

Maps für mich in Ordnung bin. –

0

Vermeiden Sie ‚virtuelle‘, wenn Sie Karten von Funktionszeiger verwenden. In diesem Zusammenhang hilft die Verwendung von "virtuellem" Schlüsselwort nicht viel. Zum Beispiel

descrToFuncMap["X"]=&MyClass::GetX; 

immer rufen 'MyClass :: GetX' -Funktion auch wenn GetX von der abgeleiteten Klasse von MyClass außer Kraft gesetzt wird.

Normalerweise haben Sie keine große Anzahl von Funktionen in der Klasse, anstatt mit map können Sie einfache struct-Array erstellen und eine for-Schleife verwenden.Wenn die Anzahl der Funktionen klein ist, gibt es keinen großen Leistungsunterschied zwischen Karte und Array. Etwas ähnlich wie Code unten funktioniert

class MyClass 
{ 
    //........ 
    double GetX(); 
    double GetSomethingElse(); 
    double GetT(); 
    double GetRR(); 
    //........ 
}; 

typedef double (MyClass::*MemFuncGetter)(); 

struct FuncTable 
{ 
    const char* m_pFuncName; 
    MemFuncGetter m_pFuncPtr; 
}; 

class Processor 
{   
public: 
     void Process(Myclass& m, string); 
}; 

static FuncTable descrToFuncMap[] 
{ 
    { "X", &MyClass::GetX}, 
    { "SomethingElse", &MyClass::GetSomethingElse }, 
    { "RR", &MyClass::GetRR}, 
    { "T", &MyClass::GetT} 
}; 

void Processor::Process(MyClass& ms, const std::string& key) 
{ 
    int functablesize = sizeof(descrToFuncMap)/sizeof(descrToFuncMap[0]) 

    for(int i=0; i< functablesize; ++i) 
    { 
     if(strcmp(key.c_str(), descrToFuncMap[i].m_pFuncName)==0) 
     { 
      MemFuncGetter memFunc=descrToFuncMap[i].m_pFuncPtr; 
      double dResult=(ms).*memFunc();  
      std::cout<<"Command="<<key<<"result="<<result<<std::end; 
      break; 
     } 
    }  
} 
+0

Warum ruft es keine überschriebene Funktion auf? –