Praxis Beispiel / Template / Metaprogrammierung
-
Ich auch mehr oder weniger.
-
Besonders Beispiel 1: RTTI ist doch so ziemlich das genaue Gegenteil von TMP. Für sowas hat man extra Traits entwickelt, um auf solchen Spaß wie typeid und dynamic_cast zu verzichten.
-
Sowas würde ich auch nicht unbedingt als klassische Metaprogrammierung ansehen, obwohl einige TMP-Elemente vorhanden sind. Typische (teilweise Extrem-)Beispiele sind Alexandrescus Modern C++ Design oder einige Bibliotheken aus Boost.
Ad aCTa schrieb:
Für sowas hat man extra Traits entwickelt, um auf solchen Spaß wie typeid und dynamic_cast zu verzichten.
Finde mal die Typ-ID zur Laufzeit mittels Traits heraus. Lichtleins Ansatz geht in die Richtung von "Multimethoden" (dynamisches Dispatching), er könnte allerdings noch generischer und effizienter sein.
-
So wie es aussieht kommt der Code wohl nicht so gut an. Hat sich dann wohl als Artikel erledigt.

Aber noch mal zurück zum Thema.
Beispiel 1
Sollte man es sich nicht so einfach wie möglich machen und die verfügbaren Mittel je nach Anwendungszweck einsetzen? Zu der besagten Liste, diese wird beim Öffnen eines Fenster aus einen Datensatz erzeugt. Beim bearbeiten werden neue Objekte in diese Liste aufgenommen, gelöscht und geändert. Ist dieser Prozess beendet und das Fenster wird geschlossen so werden aus dieser Liste wie gesagt die Verschiedenen Objektetypen geholt und in andere Container gepackt. Also 1 mal wird auf RTTI zurückgegriffen. Was soll daran schlecht sein? "Effizienter" muss es auch nicht sein. Zeit Spielt schlicht keine Rolle.
Noch ein kleiner Hinweis. Die besagte Liste ist eigentlich real ein Komplexer Struktur-Tree, der für die Bearbeitung von max. 1 Millionen Objekten ausgelegt ist. In diesen Fall wird Sie "zweckentfremdet".
Beispiel 2
Wie ich in meinen zweiten Post geschrieben habe, habe ich einmal einen XML Ähnlichen Stream Prototypen geschrieben der auf den Ideen vom "Modernes C++ Design" zurückgreift. (Typelisten) Und wie ich schon beschrieben habe, das ganze ist nicht mehr Leserlich! Man kann den Code einfach nicht Warten. Und nur weil es Cool ist (Ja es ist Cool, wenigstens wenn man es mal Programmiert
)und funktioniert sollte man es nicht einsetzen, wenn man den Code nicht Pflegen kann. Meine Meinung.
Und es würde mich freuen wenn mir Jemand ein XML Ähnlichen Stream zeigt der in der Benutzung so einfach ist wie von mir beschrieben.Lichtlein
-
Zum Beispiel 1:
Was spricht gegen einen klassischen Ansatz ala:#include<iostream> #include<vector> #include<list> #include<deque> class A; class B; class C; class Collections{ public: std::vector<A*> vecA; std::list<B*> vecB; std::deque<C*> vecC; }; class A { public: int i; virtual void assign(Collections& col){ col.vecA.push_back(this); } virtual ~A(){} }; class B : public A { virtual void assign(Collections& col){ col.vecB.push_back(this); } }; class C : public A { virtual void assign(Collections& col){ col.vecC.push_back(this); } }; int main(){ A a1; a1.i = 1; A a2; a2.i = 2; B b1; b1.i = 3; B b2; b2.i = 4; C c1; c1.i = 5; C c2; c2.i = 6; std::vector<A*> vec; vec.push_back(&a1); vec.push_back(&b1); vec.push_back(&c1); vec.push_back(&a2); vec.push_back(&b2); vec.push_back(&c2); Collections col; for(std::vector<A*>::iterator it = vec.begin(); it!=vec.end(); ++it){ (*it)->assign(col); } std::cout << "A:" << std::endl; for(std::vector<A*>::iterator it = col.vecA.begin(); it!=col.vecA.end(); ++it){ std::cout << (*it)->i << std::endl; } std::cout << "B:" << std::endl; for(std::list<B*>::iterator it = col.vecB.begin(); it!=col.vecB.end(); ++it){ std::cout << (*it)->i << std::endl; } std::cout << "C:" << std::endl; for(std::deque<C*>::iterator it = col.vecC.begin(); it!=col.vecC.end(); ++it){ std::cout << (*it)->i << std::endl; } return 0; }A:
1
2
B:
3
4
C:
5
6
-
XSpille schrieb:
Zum Beispiel 1:
Was spricht gegen einen klassischen Ansatz ala:Das ist eine gute Frage, aber noch nicht DIE Frage.
-
volkard schrieb:
XSpille schrieb:
Zum Beispiel 1:
Was spricht gegen einen klassischen Ansatz ala:Das ist eine gute Frage, aber noch nicht DIE Frage.
Was spricht gegen Templates?
-
Templates? schrieb:
volkard schrieb:
XSpille schrieb:
Zum Beispiel 1:
Was spricht gegen einen klassischen Ansatz ala:Das ist eine gute Frage, aber noch nicht DIE Frage.
Was spricht gegen Templates?
Natürlich kannst du auch Abfragen ala
if(typA){ ... } else if(typB){ ... } else if(typC){ ... }machen und was anderes macht er ja quasi nicht.
Er hat genau genommen mehrere Schleifen mit einem if drin,
die er durch Templates auslagert.
Abgesehen davon geht er dadurch noch mehrfach durch die Liste geht.
Aber der Punkt ist (wohl) relativ unerheblich.Um (u. a.) genau solche Konstrukte zu vermeiden, bietet sich ja
Objektorientierung an.
Klar kann man jedes C++ in ein C Programm umwandeln und durch die
Templates vereinfacht er sich quasi das Schreiben eines 'C-Programms'
(zumindest an dieser Stelle).
Zudem sind diese Konstrukte schwerer lesbar als der OO-Ansatz (vielleicht Geschmacksache)Ich glaube, ich würde die Objekte schon während der gesamten Zeit getrennt
verwalten und ein anderes Objekt drum wrappen, damit ich über
alle Elemente iterieren kann.
-
bin nicht so der c++ kenner, aber blähen templates die größe des kompilates nicht extrem auf?
-
@XSpille
Danke.
Mal ein richtiger Vorschlag wie man es besser/anders machen könnte. Ich habe Deine Idee mal ein wenig umgeformt so das man einzelne Werte aus der Liste abfragen kann.template< typename type > struct sSammler { std::list< type > mList; }; struct sBase { typedef void (*tFunc) (); template< typename type > void assign(sSammler< type > &Sammler); virtual tFunc assignRefFun() { return (tFunc) 0; } }; template< typename t > struct sAssign : public sBase { static void refFunc() {}; virtual tFunc assignRefFun() { return &refFunc; } }; template< typename type > void sBase::assign(sSammler< type > &Sammler) { if(&sAssign<type>::refFunc == assignRefFun()) { Sammler.mList.push_back(*static_cast<type*>(this)); } } struct sA : public sAssign<sA> {}; struct sB : public sAssign<sB> {}; struct sC : public sAssign<sC> {}; int main(int argc, char *argv[]) { typedef std::list<sBase*> tBases; tBases J; J.push_back(new sA); J.push_back(new sB); J.push_back(new sA); J.push_back(new sA); J.push_back(new sB); J.push_back(new sC); J.push_back(new sA); cout << J.size() << endl; sSammler<sA> Sa; for(tBases::iterator Iter = J.begin(); Iter != J.end(); ++Iter) { (*Iter)->assign(Sa); } cout << Sa.mList.size() << endl; return EXIT_SUCCESS; }Ok das mit dem Pointer auf eine Funktion als Erkennung ist schon sehr Grenzwertig. Man könnte hier auch mit der TypeToInt Methode Arbeiten. Würde aber wieder mehr schreibaufwand bedeuten. (Mmmm vielleicht sollte ich in mein Projekt alles eine Type Id geben)
Bitte jetzt Steinigen.

Lichtlein
-
no_c0de schrieb:
bin nicht so der c++ kenner, aber blähen templates die größe des kompilates nicht extrem auf?
Kommt drauf an, was man macht. Es kommt natürlich für jede unterschiedliche Templateinstanzierung eine neue Klasse/Funktion hinzu. Jedoch hätte man diese ja in der Regel auch ohne Templates schreiben müssen, um das gleiche zu erreichen.
Es gibt einige Extremfälle von Metaprogrammierung, wo man alle möglichen Programmabläufe durch den Compiler durchrechnen lässt. Dann hat man alle denkbaren Programmergebnisse im Compilat stehen, was natürlich extrem blähen kann (kann aber auch kürzer sein!). Dies sind dann aber wiederum die exotischen Fälle der Metaprogrammierung die in der Praxis niemand benutzt.
-
SeppJ schrieb:
no_c0de schrieb:
bin nicht so der c++ kenner, aber blähen templates die größe des kompilates nicht extrem auf?
Kommt drauf an, was man macht. Es kommt natürlich für jede unterschiedliche Templateinstanzierung eine neue Klasse/Funktion hinzu. Jedoch hätte man diese ja in der Regel auch ohne Templates schreiben müssen, um das gleiche zu erreichen.
Ich würde eher sagen: ohne Templates würde man viele Probleme anders lösen.
Viel Code der durch Templates erzeugt wird ist 1:1 gleich für alle Template-Instanzierungen. Die meisten C++ Programmierer sind aber zu bequem solchen Code aus dem Template in z.B. eine Nicht-Template-Basisklasse rauszuziehen.
An vielen Stellen wird auch nur ein T* rumgereicht, oder vielleicht ein sizeof(T) verwendet, und sonst nix gemacht was für verschiedene T unterschiedlich wäre. Das sind alles Dinge wo man ohne Templates z.B. void-Zeiger verwendet hätte, bzw. Dinge wie sizeof(T) als Parameter mitgegeben/als Member abgespeichert.
Kurz: bei dem was ich als typischen Template-Code kenne, werden Programme definitiv unnötig aufgebläht. Der Begriff "Template Bloat" wurde ja nicht umsonst geprägt.
Man kann Templates allerdings auch so einsetzen, dass der Template Bloat auf ein vernünftiges Mass reduziert wird.Wobei vernünftig natürlich wieder sehr subjektiv und auch vom Programm/Einssatzzweck abhängig. Wenn irgend ein Game/Tool/... was auf modernen Rechnern laufen soll statt 500k statt 200k hat, ist mir das herzlich egal

-
Zum Beispiel arbeiten Boosts Pointer-Container mit
void*stattT*. Ist wahnsinnig praktisch beim Debuggen, wenn man nach den etlichen Indirektionsebenen immer noch nicht auf den konkreten Wert trifft...
Indirektion von
boost::ptr_map(ohne Template-Parameter):boost::ptr_map boost::ptr_map_adapter boost::ptr_container_detail::ptr_map_adapter_base boost::ptr_container_detail::associative_ptr_container boost::ptr_container_detail::reversible_ptr_container std::mapUnd dann steht man vor dem Nichts (genauer gesagt dem Zeiger darauf).