Methoden Zeiger für unterschiedliche Rückgabewerte
-
Hallo!
Sagen wir ich habe eine Klasse mit 2 Methoden, die unterschiedliche Typen liefern:
class Person { ... public: int getAge() const; string getName() const; };Und jetzt will ich in einer Struktur beschreiben, was Person so kann und WIE man an die Werte kommt. Das soll so aussehen:
Descriptor desc[] = { { "age", getAge }, // Attribut age kann mit getAge() geholt werden { "name", getName } };Wenn die getter alle den selben Rückgabetyp hätten, wäre das ja kein Problem. Aber meine getter haben unterschiedliche Rückgabetypen und somit kann ich nicht einfach einen typedef auf einen Methodenzeiger angeben.
Wie könnte ich das lösen?
-
Lukan schrieb:
Und jetzt will ich in einer Struktur beschreiben, was Person so kann und WIE man an die Werte kommt.
Das hast du bereits fertig. Die Struktur heißt Person, da steht genau das drin was Person kann und wie man an die Werte kommt.
Willst du Reflection bauen? Per String auf die Getter zugreifen? Was soll die Struktur können was Person nicht kann?
-
Ich fülle in meinem GUI Code große Tabellen und diese Tabellen werden beschrieben mit so Descriptor structs.
Um die Zellen der Tabelle zu füllen, habe ich in etwa folgenden Code:
//--------------------------------------------------------------------------------------- QVariant PersonSet::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole || role == Qt::EditRole) { const Person& person = mPersons[index.row()]; switch(index.column()) { case 0: return person.getSymbol(); case 1: return person.getDescription(); case 2: ....Das werden bei mir leicht mal 30 cases. Könnte ich in diesen Descripter den getter der Person Klasse hinzufügen, könnte ich einfach sowas schreiben:
return toString( mDescriptor[index.column()].executeGetter() );
-
Lass deine Funktionen doch gleich ein QVariant zurückgeben.
-
Das macht keinen Sinn. Die Person ist ja das Model und soll von GUI keine Ahnung haben. Getter a la QVariant getAge() sind da absolut unschön.
-
Lukan schrieb:
Das macht keinen Sinn. Die Person ist ja das Model und soll von GUI keine Ahnung haben. Getter a la QVariant getAge() sind da absolut unschön.
Getter an sich sind unschön. Davon abgesehen ist QVariant nicht GUI.
Ich würde allerdings sowieso bei case bleiben.
-
Wie du schon gemerkt hast, kannst du Funktionen, die verschiedene Typen zurückgeben, nicht gemeinsam abspeichern.
Du willst das ja aber auch gar nicht, da du ohnehin zu einem String konvertierst. Naheliegend wäre es also, gleich Funktionen zu schreiben, die diese String-Konvertierung für jedes Objekt durchführen.
// Wrapper um Getter, der String zurückgibt typedef std::function<std::string(const Person&)> Descriptor; // Das überlädst du für verschiedene Typen X // Du kannst auch std::to_string() einsetzen und generisch arbeiten std::string toString(X value); // Gibt eine Funktion zurück, die für ein bestimmtes Attribut den // entsprechenden Getter aufruft und das Resultat zu String konvertiert template <typename R> Descriptor makeDescriptor(R (Person::*getter)() const) // getter = Memberfunktionszeiger { return [] (const Person& obj) { return toString( obj.*getter() ); }; } // Anwendung Descriptor symbol = makeDescriptor(&Person::getSymbol); Person obj = ...; std::string val = symbol(obj);
-
@Nexus: Das sieht schon mal vielversprechend aus. Wie sieht das ohne C++11 aus (ich verwende kein C++11)?
-
Nicht so gut. Es läuft wohl auf Java hinaus. Naja, wenigstens haben wir Memberfunktionszeiger

struct BaseDescriptor { virtual ~BaseDescriptor() {} virtual std::string get(Person& obj) const = 0; }; template <typename R> struct ConcreteDescriptor : BaseDescriptor { typedef R (Person::*Getter)() const; ConcreteDescriptor(Getter g) : getter(g) { } virtual std::string get(Person& obj) const { return toString( obj.*getter() ); } Getter getter; };+ ein RAII-Wrapper darum herum, damit du das auch vernünftig einsetzen kannst, ohne ständig manuelle Speicherverwaltung zu benutzen. Braucht allerdings auch noch eine
clone()-Funktion für Kopiersemantik.Wenn du TR1 hast, kannst du auch mit
std::tr1::functionundstd::tr1::bind()etwas basteln.
-
Lukan schrieb:
Könnte ich in diesen Descripter den getter der Person Klasse hinzufügen, könnte ich einfach sowas schreiben:
return toString( mDescriptor[index.column()].executeGetter() );Der getter kann nur mit einem Objekt aufgerufen werden. Soll das Objekt im Descriptor drin sein oder als Parameter übergeben werden?
Abgesehen davon könntest du sowas machen:
string getName(const Person &p){ return p.getName(); } string getAge(const Person &p){ return to_string(p.getAge()); } //pro Klasse und Attribut eine Funktion hinzufügen template <class T> string getString(int column, const T &t){ switch(column){ case 1: return getName(t); case 2: return getAge(t); //Attribute durchnummerieren } } QVariant PersonSet::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole || role == Qt::EditRole) { //const Person& person = mPersons[index.row()]; return getString(mPersons[index.row()]);Das sollte nicht so krass viel sein und du musst nur ein einziges Mal den Code schreiben.
@Nexus: Da fehlt eine Klammer Zeile 19: return toString( (obj.*getter)() );
-
@nwp3: Naja, in deinem Code habe ich ja wieder das riesige switch;)
Ich glaube das was ich wollte geht nicht so wirklich mit C++. Dafür ist C++ wohl zu stark typisiert. Dennoch danke!
-
Lukan schrieb:
Ich glaube das was ich wollte geht nicht so wirklich mit C++. Dafür ist C++ wohl zu stark typisiert.
Ich habe zwei Lösungen gezeigt, für C++98 und C++11. Warum gehen die nicht?
Hast du die Java-Bemerkung etwa wörtlich genommen?
-
Nexus schrieb:
Lukan schrieb:
Ich glaube das was ich wollte geht nicht so wirklich mit C++. Dafür ist C++ wohl zu stark typisiert.
Ich habe zwei Lösungen gezeigt, für C++98 und C++11. Warum gehen die nicht?
Hast du die Java-Bemerkung etwa wörtlich genommen?
Weil mir deine Lösung zu komplex ist. Und zusätzlich bräuchte ich noch ein clone() und noch weiteres Gedöns? Ne, das is langsam alles overkill. Dann bleib ich einfach beim switch!