Typedefs mit Kindklassen typedefs
-
Beim Herumexperimentieren, habe ich folgenden Code erzeugt.
Ich würd gern wissen obs totaler Quatsch ist, oder ob man diesen
Code irgendwie zum laufen bekommen kann.
( Ob mans dann benutzen will, ist sicherlich noch eine andere Frage )template<class T> class Foo{ typedef typename T::mytype super_type; }; class Bar : Foo<Bar>{ typedef int mytype; }; int main() { Bar bar; return 0; }Fehlermeldung:
3: invalid use of incomplete type »class Bar«
5: forward declaration of »class Bar«Soweit ich den Compiler verstehe ist dass Problem, dass ich in der Elternklasse
( Foo ) nicht auf die typedefs von Bar zugreifen kann, weil die Klasse
noch nicht definiert ist.
Andersherum gehts natürlich auch nicht, da ich ( imho ) nicht von Klasse
erben kann, die nicht definiert sind.
( Also eine Art Zirkuläre Referenz )..//edit
Merkwürdig ist auch noch, dass der Compiler, sobald das typedef ( Zeile 3 ) vorhanden ist, eine forward declaration bemängelt.
Dies tut er natürlich nicht, wenn das typedef nicht da ist.
Irgendwie scheint er dies aufgrund der Abhängigkeiten "falsch zu verstehen".
Diese Vermutung gebe ich aber gerne an die Compilerkenner weiter.
-
Eine Möglichkeit wäre es, die typedefs in eine "traits" struct auszulagern.
Wobei diese nur der typedefs und nicht für zB algorithmen dient.Somit könnte ich eine allgemeine struct machen und für jedes Bar
eine template-spezialisierung.Dies ist aber nur eine Notlösung.
In meinem Projekt soll es viele verschiedene "Bar"s geben können.
Diese sollen auch "unabhängig" von der Basisklasse Foo entwickelt werden.Wenn jedes Bar nun eine eigene typedef struct-spezialisierung implementiert, ist dies nicht wirklich schön, da die ganzen Spezialisierungen in diversen Modulen ( Header- und Quelldateien ) vorliegen..
Nochmal etwas zum Hintergrund.
Es soll viele verschiedene Klassen wie Bar geben ( also erbende Klassen ).Alle sollen von Foo erben, allerdings braucht Foo, um ein allgemeines interface
zu definieren ( Also abstrakte Methoden ), Kenntniss von Typen die
innherhalb der Kindklassen ( den "Bar"s ) definiert werden.Vielleicht hat jemand ja eine schöne(re) Idee.
//edit
Weitere Nachteile der "typedef struct" sind zB dass ich in der erbenden "Bar"
Klasse die gleichen typedefs brauche. Somit muss ich umständlich über die struct zugreifen, oder doppelt definieren, was wiederrum schlecht wäre.
Alternativ könnte ich auch statt einer Spezialisierung, structs machen,
die keine Templates sind, aber viel schöner scheint mir das auch nicht ( und es löst bis auf die Verteilung eines "types" in viele Dateien auch keine Probleme )
//edit 2
Beispielcodetemplate<class traits> class Foo{ typedef typename traits::traits_type super_type; }; class Bar1; class Bar2 template<class bar> struct bar_traits{ typedef typename bar::mytype traits_type; }; template<> struct bar_traits<Bar1>{ typedef int traits_type; }; class Bar1 : Foo<bar_traits<Bar1> >{ }; template<> struct bar_traits<Bar2>{ typedef float traits_type; }; class Bar2 : Foo<bar_traits<Bar2> >{ };
-
Also mir fallen hier zwei Möglichkeiten ein:
template <class T> class Foo : public T { typename T::mytype test() {} }; class BarTypes { public: typedef int mytype; }; class Bar : public Foo<BarTypes> { public: mytype test2() {} }; /////////////////////////////////////// template <class T> class Foo2 { public: typedef T mytype; mytype test() {} }; class Bar2 : public Foo2<int> { public: mytype test2() {} }; /////////////////////////////////////// int main() { Bar b; Bar2 b2; return 0; }MSVC spuckt im Übrigen auch sehr seltsame Fehlermeldungen aus wenn ich ihm dein 1. Beispiel füttere (allerdings ganz andere als du schreibst). Hängt vermutlich ganz davon ab wie im Compiler Templates implementiert sind.
----
Interessant wäre noch was du da machen willst -- mit deiner abstrakten Beschreibung kann ich mir irgendwie nichts vorstellen. Zumindest nichts wo ich nicht sowieso Variante 2 (Foo2, Bar2) in meinem Beispiel verwendet hätte.
-
Hallo und danke für die Antwort.
Es gibt eine BasisKlasse ( Base ). Diese hat nur den Zweck gemeinsame
Funktionen zu definieren und einige als abstrakt zu deklarieren.
Soweit so gut ( bisher sind auch keine Templates oder so notwendig ).Nun ist es so, dass die erbenden Klassen sich dadurch unterscheiden, dass
sie von bestimmten typen wissen. Dieser Typ ist ein konkreter "result_type",
welche in einer Methode zurückgegeben wird.
Beispiel:class foo_result{ ... }; class foo_handler : public base{ public: typedef foo_result result_type; const result_type calculate(); }; class bar_result { ... }; class bar_handler { typedef bar_result result_type; const result_type calculate(); };Soweit so gut ( und noch kein Problem ).
Nun möchte ich aber dass alle, die einen Handler hinzufügen, oder eine
Klasse vom Handler ableiten, bestimmte Methoden implementieren sollen.
Alle handler sollen zB die Methode "calculate" haben.
Deswegen würde ich dies gern in der Base als abstrakt definieren.
Dies kann ich aber nur, wenn ich a) auf die typedefs der erbenden
Klasse zugreife, oder ein Objekt mit typedefs übergebe.template<class child> class base{ public: typedef child::result_type result; // compiler error const result calculate()=0; }; // oder template<class types> class base{ public: typedef types::result_type result; const gresult calculate()=0; };Für die "types" muss ich dann sowas machen ( bisher meine einzige Idee )
template<class child> class types{ // typedefs nach Konvention // .... }; template<> class types<bar_handler>{ typedef bar_result result_type; }; template<> class types<foo_handler>{ typedef foo_result result_type; };Leider kann ich nicht einfach eine struct types machen,
die ohne template-spezialsierung auf typedefs zugreift:class foo_result { ... }; class foo_handler : public base{ public: typedef foo_result result_type; const result_type calculate(); }; class bar_result { ... }; class bar_handler { typedef bar_result result_type; const result_type calculate(); }; template<class handler> class types{ typedef handler::result_type result; }; template<class types> class base{ typedef types::result result; const result calculate()=0; };Hier geht wieder im Kreis und der alte Fehler taucht wieder auf,
denn beiclass foo_handler: public base <types<foo_handler> > {};hat man ( bzw ich ) wieder das Ursprungsverhalten.
Ein Problem der Templatespezialisierung ist aber, dass überall in den
Quelldateien für jeweilige "handler" Spezialisierungen der Klasse types sind. Angenommen es gibt im Projektverzeichnis sowas hier:include/app/handler/foo_handler/
include/app/handler/bar_handler/Dann wären die Definitionen von Types in diesen Unterverzeichnissen
aufgeteilt.
Grund dafür ist, dass jemand einen handler hinzufügen können soll, ohne
Code ausserhalb des Verzichnisses zu ändern. ( Deswegen kann man
nicht einfach ne zentrale Datei types machn, in die jede Spezialisierung
geschrieben wird ).
Hinzu kommt, dass ich gern eine Lösung ohne Spezialisierung haben würde.
Die handler sind so geschrieben, dass man von Ihnen ableiten kann.
Sprich wenn jemanden der foo_handler nicht gut genug ist, kann er einfach
seinen foo2_handler von foo ableiten und kann weiter machen.
Dann braucht er sicherlich aber auch nen eigenen result_type ( foo2_result ) somit also auch wieder eigene typedefs und somit wieder template-spezialisierungen.Natürlich kann man sagen, dass ich einfach auf die abstrakte Methode
verzichten kann.
Aber genau dass ist ja meine Eingangsfrage.
Angenommen ich will dies Lösen, wie kann man dies geschickt machen. ( Ob mans dann nutzt muss man sehen ).
-
wenn man keine probleme hat nimmt man einfach C++
-
Ich verstehe nicht wieso du das so kompliziert machen willst...
class handler_base { public: // common stuff }; template <class T> class handler : public handler_base { public: typedef T result_type; virtual result_type calculate() const = 0; }; class foo_handler : public handler<int> { public: virtual result_type calculate() const { return 123; } };Einfacher gehts nicht.
Die handler sind so geschrieben, dass man von Ihnen ableiten kann.
Sprich wenn jemanden der foo_handler nicht gut genug ist, kann er einfach
seinen foo2_handler von foo ableiten und kann weiter machen.Ich würde hier Komposition verwenden und nicht Vererbung. Das löst dein Template-Problem, und ist davon abgesehen wohl auch die bessere Lösung.
-
hustbaer schrieb:
Ich verstehe nicht wieso du das so kompliziert machen willst...
class handler_base { public: // common stuff }; template <class T> class handler : public handler_base { public: typedef T result_type; virtual result_type calculate() const = 0; }; class foo_handler : public handler<int> { public: virtual result_type calculate() const { return 123; } };Einfacher gehts nicht.
So hatte ich es ursprünglich.
Dann kam aber ein weiterer Unterschied hinzu.
Also die handler unterscheiden sich in zB 3 Typen.
Ein Result, ein Handle und ein Statement .
Somit gibt es 3 Funktionen die alle einen unterschiedlichen Rückgabewert haben.
Deswegen kann ich nicht direkt den result_type als Template Argument nehmen.
Ich kann natürlich einfach soviele Template-Parameter nehmen, wie ich brauche,
um alle sich unterscheidenen Rückgabewerte abzufangen. Dies gefiel mir aber
nicht so richtig. Meine Überlegung war:Wenn der Handler die Sache ist, bzw alles weiss,
worin sich die abstrakten Funktionen der Base unterscheiden,
kann ich diese bzw "ihr Wissen" ( in form von typedefs ) ja einfach
der Basisklasse übergeben und schon kann sie darauf zugreifen.
-
ch kann natürlich einfach soviele Template-Parameter nehmen, wie ich brauche,
um alle sich unterscheidenen Rückgabewerte abzufangen.Genau. Mach das so.
Wenn der Handler die Sache ist, bzw alles weiss,
worin sich die abstrakten Funktionen der Base unterscheiden,
kann ich diese bzw "ihr Wissen" ( in form von typedefs ) ja einfach
der Basisklasse übergeben und schon kann sie darauf zugreifen.Finde ich nicht so gut, ganz abgesehen davon dass es nur mit hässlichen Workarounds geht. Wenn du 10 Handler hast, die alle z.B. "int, int, double" verwenden, dann würdest du so 10 Template-Instanzierungen machen, die aber in Wirklichkeit alle gleich sind.
-
Danke für die Antwort..
hustbaer schrieb:
Wenn der Handler die Sache ist, bzw alles weiss,
worin sich die abstrakten Funktionen der Base unterscheiden,
kann ich diese bzw "ihr Wissen" ( in form von typedefs ) ja einfach
der Basisklasse übergeben und schon kann sie darauf zugreifen.Finde ich nicht so gut, ganz abgesehen davon dass es nur mit hässlichen Workarounds geht. Wenn du 10 Handler hast, die alle z.B. "int, int, double" verwenden, dann würdest du so 10 Template-Instanzierungen machen, die aber in Wirklichkeit alle gleich sind.
Ja, das habe ich nicht bedacht. Aucn wenn ich Grundsätzlich verschiedene
Typen habe, kann das uA bei Erbenden Handlern zu einem Problem werden.Dementsprechend kann ich dir zustimmen und werd wohl mehrere Templateargumente
nehmen.Eine Kleinigkeit noch als Zusatz.
Kannst du mir ggf deine Fehlermeldung und die Compilerversion schreiben ?
"Meine Fehlerversion" konnte ich mit gcc 4.3.2 und gcc 4.1.2 erzeugen.Wäre interessant zu sehen, warum es da genau zu diesem Fehler kommt.