2017-03-15 4 views
1

Ich erstelle eine Klasse namens MessageableObject, die eigene Memberfunktionen in einer Zuordnung registrieren kann. Diese Funktionen können von außen aufgerufen werden, indem sendMessage aufgerufen wird, ein Rückgabetyp als Vorlagenparameter bereitgestellt wird und der Name der Funktion sowie die Parameter als variadische Argumente übergeben werden. Ich habe etwas gesehen, das dem vorher sehr ähnlich ist, nur die Funktionen waren nicht Mitgliedsfunktionen der Klasse, die sie speichert.Speichern und Aufrufen einer unordered_map von Memberfunktionen mit unterschiedlichen Signaturen

Unten ist der Code, den ich bisher geschrieben habe, sowie ein Testfall, der erfolgreich sein sollte.

#include <iostream> 
#include <typeindex> 
#include <typeinfo> 
#include <unordered_map> 

class MessageableObject 
{ 
    using _message_ptr = void(MessageableObject::*)(void); 

    std::unordered_map<const char*, std::pair<_message_ptr, std::type_index>> _message_map; 

public: 

    MessageableObject() 
    { 
    } 

    template<typename F> 
    void addMessage(const char* name, F func) 
    { 
     auto type = std::type_index(typeid(func)); 
     _message_map.insert(std::make_pair(name, std::make_pair((_message_ptr)func, type))); 
    } 

    template<typename R, typename... Args> 
    R sendMessage(const char* name, Args&&... args) 
    { 
     auto iter = _message_map.find(name); 
     assert(iter != _message_map.end()); 
     auto type = iter->second; 
     auto func = reinterpret_cast<R(MessageableObject::*)(Args ...)>(type.first); 
     assert(type.second == std::type_index(typeid(func))); 
     return func(std::forward<Args>(args)...); 
    } 

    void foo() 
    { 
     std::cout << __FUNCTION__ << std::endl; 
    } 

    int bar(int i, int j) 
    { 
     std::cout << __FUNCTION__ << ' ' << i + j << std::endl; 
     return i + j; 
    } 
}; 

int main() 
{ 
    MessageableObject obj; 
    obj.addMessage("foo", &MessageableObject::foo); 
    obj.addMessage("bar", &MessageableObject::bar); 

    obj.sendMessage<void>("foo"); 
    int baz = obj.sendMessage<int>("bar", 1, 2); 

    return 0; 
} 

Derzeit erzeugt dieser Code den folgenden Fehler zweimal:

C2064: Ausdruck ergibt keine zu einer Funktion unter n Argumente

Das erste Mal mit n 0 ist wenn ich versuche, foo anzurufen, und n 2 ist, wenn ich versuche, bar aufzurufen. Warum treten diese Fehler auf? Und kann ich das auch mit der aktuellen Syntax umsetzen? Jedwedes https://stackoverflow.com/a/33837343/962805

Hilfe geschätzt stark:

Als Referenz die Version dieser Implementierung, die auf nicht-Member-Funktionen arbeitet, kann hier gefunden werden. Ich bin nicht sehr gut informiert über Typ-Löschung und Bindung von Member-Funktionen. Die Absicht dafür ist, die Funktion GameObject.sendMessage für dynamische Nachrichtenübermittlung der Einheitseinheit zu erstellen.

Aktualisierung: Wie nach der Antwort von skypjack unten wird die obige Lösung brechen, wenn der Nachrichtenaufruf eine Variable enthält. Sie kann behoben werden, indem Sie in sendMessage Args&&... in Args... ändern, jedoch stellt dies ein neues Problem dar: Wenn der Map eine Nachricht hinzugefügt wird, die einen Referenzparameter enthält (z. B. int bar(int&, int)), kann sie nicht implizit aufgerufen werden. Der Aufruf von sendMessage<int, int&, int>("bar", x, 2) funktioniert wie vorgesehen.

Antwort

2

Wenn Sie umfassen <cassert> am Anfang der Datei und verwenden diese:

return (this->*func)(std::forward<Args>(args)...); 

Statt dessen:

return func(std::forward<Args>(args)...); 

Es kompiliert.

Ich kann nicht sagen, dass es funktioniert für sie mir sieht man zum Glück sind die Vermeidung einer UB und es funktioniert anscheinend (das ist eine absolut gültige UB) wäre besser geeignet.
Das Problem ist, dass bar Typ hat:

int(MessageableObject::*)(int i, int j); 

Und Sie werfen es zu gieße:

void(MessageableObject::*)(void); 

Dann Sie werfen es zurück, um es für Sie es, wie sie Berufung auf richtige Art ist:

Wenn Sie stattdessen dies getan haben:

int x = 1; 
obj.sendMessage<int>("bar", x, 2); 

Sie würden eine Besetzung zu ausgeführt haben:

int (MessageableObject::*)(int &i, int j); 

Etwas Ähnliches mit einem falsch Rückgabetyp passieren würde. All das sind UB. Daher eine zu schwache Lösung, um es in einer Produktionsumgebung aus meiner Sicht zu verwenden.

+0

Vielen Dank! Diese Lösung kompiliert und funktioniert tatsächlich. Jetzt muss ich nur eine robustere Lösung finden, um Variablen als Argumente zu behandeln und UB zu vermeiden. – dyeo

Verwandte Themen