Template-basierende Funktion mit Typecasting? ("Generika") (gelöst)
-
Hallo,
aktuell spiele ein bisschen mit Templates und tappe in die Falle, dass Templates != Generische Typen sind. Hier mal ein Beispielcode, den ich nicht zum Laufen kriege:
class Foo { public: template<typename _Ty> _Ty get(bool b) { if(b) return this->s; else return this->i; } private: string s; int i; }; int main() { Foo *f = new Foo; // unabhängig von den Werten in den Variablen: f->get<string>(true); // Fehler, da Foo::s string ist, aber Foo::i nicht! f->get<int>(false); // Genau umgekehrt! }Den Fehler verstehe ich ja, aber habt ihr Rat, wie man das elegant mit dem Typecasting umsetzen kann? Weil ich eigentlich tatsächlich das Zurückbekommen will, was ich auch als Templateparameter angebe.
Gruß
-
Eine Funktion kann nur einen Rückgabetyp haben - und eine Funktion, die wahlweise zwei grundsätzlich inkompatible Typen zurückgeben könnte, wird nicht viel Sinn machen. (stell dir mal vor, was
get<string>(true)liefern sollte ;))Kannst du mal erläutern, was du mit dem Code (inhaltlich) erreichen willst - vielleicht können wir dann bessere Lösungen anbieten.
-
Aktuell sollen verschiedene Objekte datenbankähnlich serialisiert werden. Dazu habe ich eine Database Abstraction Layer und versuche mich am ersten Teil: everything-in-memory. Die Klasse Table (in Wirklichkeit ist es keine -.-") bekommt dann gesagt, welchen Typen sie aufzunehmen hat (Bsp.: User*) und in welcher Datenstruktur das gehandled wird:
template<template<typename> class Container, typename Type> class Table { private: Container<Type> *data; /* ... */ };Jedoch versuche ich die einzelnen Klassen, aus denen persistente Objekte erstellt werden, weiter zu abstrahieren, dass man nicht auf Properties/Variablen zugreifen muss sondern theoretisch gesehen Calls wie den folgenden machen könnte:
userObjectOutOfATable->get<string>(User::Name);wobei das
User::Nameaus einer Enumeration kommt und ich so (anstatt einen String zu übergeben) sagen kann welche Variable etc. ich gerade will. Dies möchte ich dann so beispielsweise in einem InterfaceIPersistentObjectunterbringen, falls es denn klappen sollte und andererseits eine gute Idee ist.Ist so klarer geworden, was ich aktuell bezwecken möchte? (Auch wenn es Komplikationen bei den Rückgabetypen gäbe, wenn der Programmieren genau aufpasst, was er macht sollte es doch egal sein xD) Gäbe es nicht eine Möglichkeit die anderen Codepfade on-the-fly zu "fake"casten? Aufruf einer Casting-Funktion die je nach Typ einfach
return NULLoder so macht xD
-
Schau dir mal die Bibliothek Boost.Any an.
Damit könntest du eine
std::map<TypeEnum, boost::any>haben und versuchen,boost::any_castauf die Map-Einträge anzuwenden. Im Fehlerfall (Eintrag nicht gefunden oder falscher Typ) wird ein Nullzeiger zurückgegeben.
-
Nexus schrieb:
Schau dir mal die Bibliothek Boost.Any an.
Damit könntest du eine
std::map<TypeEnum, boost::any>haben und versuchen,boost::any_castauf die Map-Einträge anzuwenden. Im Fehlerfall (Eintrag nicht gefunden oder falscher Typ) wird ein Nullzeiger zurückgegeben.Beim einem falschen Typen wird meines wissens nach eine Exception geworfen, ein bad_any_cast oder so ähnlich.
Und wenn der Map-Eintrag nicht gefunden wird, wird der end-Iterator zurückgegeben.
-
314159265358979 schrieb:
Beim einem falschen Typen wird meines wissens nach eine Exception geworfen, ein bad_any_cast oder so ähnlich.
Bei Zeigern nicht, siehe hier.
314159265358979 schrieb:
Und wenn der Map-Eintrag nicht gefunden wird, wird der end-Iterator zurückgegeben.
Was niemanden daran hindert, in diesem Fall einen Nullzeiger zurückzugeben.
-
Das ist mir neu, danke für den Hinweis.

-
Es funktioniert. An sich wollte ich jetzt nicht noch extra boost mit einbinden, aber was solls
Nun muss ich mir noch eine passende Abstraktion suchen.Danke nochmals!
-
Du kannst
boost::anyauch mehr oder weniger nachbauen, nur gibts es halt schon fertig in Boost.Was für eine Abstraktion meinst du? Du hast wahrscheinlich eine Funktion in der Art, oder?
template <typename T> T* GetValue(TypeEnum type);
-
Mit Abstraktion meine ich, meine Klassen der persistenten Objekte so zu umschreiben um ein Interface oder eine abstrakte Klasse da herauszuziehen als Oberklasse. Zum Selberschreiben von boost::any: Ich habe es versucht, hatte aber meine Probleme xD
-
Ich hab dir hier mal ein any geschrieben: http://ideone.com/Ye7tj
Die Sonderbehandlung von Zeigern (Zurückgeben von Nullzeigern) darfst du selbst hinzufügen, und Move-Semantiken, falls du sie brauchst, ebenfalls.
-
Ich danke dir zunächst für deine schlankere Version, die zufriedenstellend ist

Nur würde ich gerne wissen, welche Sonderbehandlungen und Move-Semantiken du meinst
-
Wie Nexus mich durch diesen Link eines besseren belehrt hat, wird im Falle, dass man versucht in einen Zeiger zu casten, aber man den falschen Typ angibt, ein Nullzeiger zurück gegeben. Meiner Implementierung wirft einen std::bad_cast, wie bei nicht-Zeigertypen.
Mit Move-Semantiken ist das Feature des nächsten Standards gemeint, mit dem man teure Kopien vermeiden kann. Will ich zum Beispiel alle Elemente aus einem std::vector in einen anderen std::vector hineinbekommen, dann wird im Normalfall der alte vector kopiert und danach gelöscht. Stattdessen könnte man die Elemente direkt in den neuen vector speichern - ohne unnötige Kopie, die sehr teuer sein kann.
Ich habe dir einmal die Zeigerbehandlung ergänzt, ich nehme nicht an, dass du die Move-Semantiken brauchst: http://ideone.com/SxdAA
Zur main: Hier wird 2 mal die selbe Adresse ausgegeben, was bedeutet, dass der Cast geglückt ist. Beim Cast auf double* wird 0 zurückgegeben, da der Typ im Objekt int* und nicht double* ist.