2012-04-10 18 views
6

Lets sagen, ich habe eine Klasse Handler mit einigen Unterklassen wie stringhandler, SomeTypeHandler, AnotherTypeHandler. Der Klassenhandler definiert eine Methode "handle" als eine gemeinsame Schnittstelle für alle Unterklassen. Die Logik zum "Handhaben" ist natürlich für die verschiedenen Handler völlig unterschiedlich.Wie kann ich "Jede Art von Daten" an eine Funktion in C++ übergeben

Also was ich tun muss, ist einen Wert von irgendetwas an die Handle-Methode übergeben. Die spezifischen Klassen können dann das "alles" auf den erwarteten Typ umwandeln.

Im Grunde, was ich brauche, ist so etwas wie das Java-Klasse Objekt: D

Das erste, was ich versuchte, war ein void*, aber anscheinend kann man nicht B* someB = dynamic_cast<B*>(theVoidPointer) tun, so dass es kein Glück.

Meine zweite Idee war, boost::any zu verwenden. Voraussetzung für die Verwendung von boost :: any ist jedoch, dass der Wert cunstructable kopiert werden muss, was bei meinen Daten nicht der Fall ist.

Irgendwelche Ideen, um das zum Laufen zu bringen?

Dank

EDIT: Beachten Sie, dass ich weiß, dass ich eine some Klasse ohne Mitglieder überhaupt nutzen könnte, und meine Daten Subklassen davon sein lassen, aber ich bin für einen allgemeineren Ansatz suchen, die mich nicht erfordern um meine eigene Wrapperklasse zu erstellen.

+2

Sind Sie sicher, dass Sie 'dynamic_cast' brauchen und' 'reinterpret_cast' nicht verwenden können? –

+1

Wie wäre es mit Vorlagen? –

+0

Es sieht so aus, als ob die meisten Ihrer Optionen hier beschrieben sind: http://stackoverflow.com/questions/913505/casting-void-pointers-depending-on-data-c – briantyler

Antwort

1

Okay, hier ist ein einfacher Ansatz boost :: any Zeiger auf Ihre Datentypen zu halten. Achten Sie jedoch darauf, dass boost :: any etwas Overhead-Code hinzufügt, was die Leistung leicht mindert (in den meisten Fällen vernachlässigbar). Ziehen Sie stattdessen boost :: spirit :: hold_any in Betracht oder void *, wenn Sie keine Typensicherheit benötigen.

class Handler { 
public: 
    void handle(boost::any value) { 
     do_handle(value); 
    } 
private: 
    virtual void do_handle(boost::any& value) = 0; 
}; 

template<class T> 
class HandlerBase : public Handler { 
private: 
    void do_handle(boost::any& value) { 
     // here you should check if value holds type T*... 
     handle_type(*(boost::any_cast<T*>(value))); 
    } 

    void handle_type(const T& value) = 0; 
}; 

class StringHandler : HandlerBase<std::string> { 
private: 
    void handle_type(const std::string& value) { 
     // do stuff 
    } 
}; 

Jetzt können Sie viele Handler-Klassen schreiben, von HandlerBase abzuleiten, ohne davon aus, dass die Handhabung Typen eine gemeinsame Basisklasse haben.

+0

Dies ist die Lösung, die ich verwendet habe, mit Ausnahme der Vorlagen-HandlerBase. Ich habe das vergessen. Ich benutze nur die spezifischen Handler, die zu dem Typ gewandert sind, den sie brauchen. Sobald ich mehrere Handler für den gleichen Datentyp habe, werde ich die templated Basis dazwischen stellen. Vielen Dank! –

-1

können Sie

tun
B* someB = static_cast<B*>(theVoidPointer); 
+2

'static_cast' bitte. –

+0

@ R.MartinhoFernandes und BertR Könnten Sie bitte auf die Unterschiede zwischen Reinterpret, statisch und dynamisch eingehen? Ich dachte, Neuinterpretation und Statik hätten einige Gefahren. –

+0

@ W.Goeman Siehe diese vorherige Frage: http://StackOverflow.com/a/332086/46642 –

0

Sie benötigen eine Basisklasse haben Datenobjekt oder etwas genannt. Alle Ihre Datentypen (string, number, whatnot) sind Unterklassen von DataObject. Sie definieren Handle wie folgt:

void Handle(DataObject *dataObject); 

Dies ist eine viel sicherere Möglichkeit zu tun, was Sie wollen. Um es noch besser zu machen, kann das DataObject sogar wissen, welche Art von Daten es enthält. Dann können die Handler überprüfen, ob ihnen die richtigen Daten gesendet wurden.

+0

Dies ist im Grunde, was ich hinzugefügt habe in meiner Bearbeitung. Dies scheint in der Tat eine gute Lösung zu sein, aber ich war auf der Suche nach einem allgemeineren Ansatz. Ich behalte es immer noch in meiner Liste von Optionen für den Fall, dass die anderen Lösungen nicht funktionieren. –

1

Sie können zum Beispiel eine Basisklasse definieren:

class BaseHandlerData { 
    ... 
}; 

Dann spezifische Datenklassen ableiten, die von Ihrem Handler erwartet:

class StringData: public BaseHandlerData { 
    ... 
}; 

class SomeData: public BaseHandlerData { 
    ... 
}; 

Dann sollten Sie BaseHandlerData passieren können, * Argument mit dem Griff Methode, und verwenden Sie so etwas wie:

void handle(BaseHandlerData *data) { 
    StringData* stringData = dynamic_cast<StringData*>(...); 
    // handle your string data here ... 
} 

zu sicher Stellen Sie den erwarteten Datentyp ein.

Gerald

+0

Das ist im Detail was ich in meinem Schnitt kurz gesagt habe. Eine gute Lösung, aber dafür schreibe ich lieber nicht die Extraklasse. Ich habe es eher als ein grundlegendes Dienstprogramm (wie boost :: any). –