virtual template member function workaround gesucht
-
dot schrieb:
GorbGorb schrieb:
Statt Hilfe kommt nur dummes Gelaber von language lawyern (naja, eigentlich nur einem) der keinem was bringt... wirklich unnötig.
Ja. Das Problem ist nur, dass man hier ohne genauere Information über die konkrete Anwendung keine hilfreiche Antwort geben kann. Denn eine gute Lösung für das Problem würde vermutlich eine grundlegende Änderung des Designs bedeuten. Und wie diese jetzt genau aussehen könnte kann man so allgemein nicht sagen...
Brauchst dich nicht angegriffen fühlen, ich hab dich ja gar nicht gemeint

Der TE meinte ja, es sei ein gewachsenes System, er kann vom design her wohl nicht viel ändern ohne Arbeit. Sein Problem ist diese Funktion, und ich denke da kann man ja was tun (siehe hustbaers und meine Vorschläge).EDIT: hustbaer, wenn ich das richtig verstanden habe spart
has(Skill* concreteSkill)den dynamic cast, wenn concreteSkill eines der gesuchten Objekte ist, oder? Dürften nicht vielmehr die dynamic_casts mit negativem Ergebnis das bottleneck sein?
-
Ich fühl mich auch nicht angegriffen, ich wollt eher indirekt dem Threadersteller sagen, das er uns konkrete Information geben soll, wenn er konkrete Antworten will

-
Muaha.
Hier stand Müll
-
GorbGorb schrieb:
EDIT: hustbaer, wenn ich das richtig verstanden habe spart
has(Skill* concreteSkill)den dynamic cast, wenn concreteSkill eines der gesuchten Objekte ist, oder? Dürften nicht vielmehr die dynamic_casts mit negativem Ergebnis das bottleneck sein?Nö, nicht ganz.
Der dynamic_cast wird pro Kombination aus konkreter Skill-Klasse und Skill-Interface nur 1x ausgeführt.Wenn also die Anfrage reinkommt "hat Objekt a das Interface X?".
Dann hole ich mir per typeid der "most derived Type" von a - nennen wir den "A".Dann gucke ich ob ich die Kombination "A, X" schon mit dynamic_cast ausprobiert wurde.
Wenn ja gibt es einen Eintrag in der map, der Wert ist dann das Ergebnis (also true heisst A erbt von X, false heisst A erbt nicht von X).Wenn nicht, dann wird das Ergebnis über dynamic_cast ermittelt, und in der Map eingetragen.
Der "Trick" (Hack) dabei ist nun dass ich intptr_t eintrage, die aus der Adresse der über typeid() ermittelten type_info Instanz erzeugt werden.
Das setzt natürlich voraus, dass a) beim reinterpret_cast keine Informationen verloren gehen. Genau dafür ist intptr_t aber da, d.h. das müsste hinhauen.
Weiters setzt es voraus, dass an einer Adresse einer type_info Instanz in Zukunft nie nie niemals eine andere type_info Instanz stehen kann.
Der Standard garantiert das soweit ich weiss nicht, aber so lange man keine DLLs/SOs dynamisch entlädt funktioniert es in der Praxis zuverlässig.
-
So, 2. Versuch ohne Template Bloat:
class Container { std::vector<Skill*> skills; typedef std::pair<intptr_t, intptr_t> SkillMapKey; typedef std::map<SkillMapKey, bool> SkillMap; // evtl. hash_map verwenden wenns hier wirklich viele Einträge geben sollte SkillMap skillMap; template <typename T> static bool interface_test(Skill* concreteSkill) { return dynamic_cast<T*>(concreteSkill) != 0; } typedef bool interface_test_fun(Skill*); bool has_impl(Skill* concreteSkill, intptr_t interfaceTypeID, interface_test_fun* tester) { SkillMapKey key(reinterpret_cast<intptr_t>(&typeid(*concreteSkill), interfaceTypeID); SkillMap::iterator it = skillMap.find(key); if (it != skillMap.end()) return it->second; else { bool result = tester(concreteSkill); skillMap[key] = result; return result; } } bool has_impl(type_info const& interfaceType, interface_test_fun* tester) { intptr_t interfaceTypeID = reinterpret_cast<intptr_t>(&interfaceType); for_each(skill, skills) { // hier jetzt hoffentlich schneller if (has_impl(skill, interfaceTypeID, tester)) return true; } return false; } template <typename T> bool has() { return has_impl(typeid(T), &Container::interface_test<T>); } }
-
C++ kennt kein for_each als Sprachkonstrukt und main muss int zurückgeben. Hässlicher MSVC Schrott.
Das bezog sich auch auf for_each du Pfeife. Lern Texte verstehen.
Lern du erstmal Texte schreiben, du Pfeife.
Relevant: http://xkcd.com/1028/
-
Danke schonmal für die vielen Anregungen.
Das mit dem for_each hatte ich nur zur anschaulichen Vereinfachung eingebaut.Mir schwebt eigentlich eher sowas vor, wie jeder Klasse eine eindeutige ID/Enum zu geben. Dann könnte man sowas machen wie:
template <typename T> bool has() const { return (T::ID == this->ID()); }Mit dem Ziel die ID zur Compile-Zeit zu vergeben (notfalls muss man die Klassen-IDs in einem anderen Header pflegen, als Enum).
Über die "letzte" Idee von hustbär hatte ich auch schon nachgedacht, das Problem von virtual templates durch eine weitere Indirektion zu lösen.
Wenn der Lookup der typeid schneller ist als dynamic_cast, kann man für eine reine has-Prüfung auch auf die typeid umsteigen.
-
Erklär uns doch mal, um was für eine konkrete Problemstellung es da eigentlich geht...
-
//TypeID.hpp extern int t_id_counter; template<typename T> int GetTypeID() { static int i = -1; if(i == -1) i = t_id_counter++; return i; } //TypeID.cpp #include "TypeID.hpp" int t_id_counter = 0;EDIT: Ist zwar nicht Compile-Zeit, dafür aber der Runtime-Aufwand zu vernachlässigen. Eine Compilezeit-ID wird wohl nur mit irgendwelchem typeid()-Kram (und damit nicht standardkonform) generierbar sein (vielleicht erst mit C++11), aber dann auch nur, wenn man aus typeid(T).name() eine eindeutige ID generiert, die demzufolge länger als int wäre...
Und was spricht gegen einen Referenzzähler?
-
nurf schrieb:
Mir schwebt eigentlich eher sowas vor, wie jeder Klasse eine eindeutige ID/Enum zu geben. Dann könnte man sowas machen wie:
template <typename T> bool has() const { return (T::ID == this->ID()); }dynamic_cast tut aber mehr, als nur eine id vergleichen. Folgendes Szenario:
A B : A C : B container.add( C() ); container.has< B >();Wird mit dieser Methode false ergeben. Was du tun könntest, ist ein bitset zu verwenden, in das sich jede base class an der Stelle Base::ID einträgt. Dazu müsstest du aber wieder die alten header files verändern oder jede Vererbungsbeziehnung manuell irgendwie neu bekannt machen (um diesen Schritt kommst du aber nicht rum, wenn du rtti komplett ersetzen willst, du musst über die Vererbungsbeziehnungen irgendwie Bescheid wissen).
-
GorbGorb schrieb:
Wird mit dieser Methode false ergeben. Was du tun könntest, ist ein bitset zu verwenden,
Oder noch einfacher:
Statt getID() eine Funktion hasID() verwenden die an den parent weiter reicht wenn false rauskommt
Aber wir brauchen eine genaue definition der Anforderung um eine gute Antwort geben zu koennen.
-
Shade Of Mine schrieb:
Aber wir brauchen eine genaue definition der Anforderung um eine gute Antwort geben zu koennen.
Prinzipiell stehen die Anforderungen im ersten Post.
Das Kind ist in den Brunnen gefallen, weil das ganze Design und nun außerdem die Performance schlecht ist. Profiling-Analyse sagt dynamic_cast zieht zu viel CPU-Last.
Wie erwähnt geht um viele Klassen, die in 2. oder 3. Stufe von der Basis-Klasse abgeleitet sind.
**Ziel:**Performance verbessen, bspw. den dynamic_cast irgendwie obsolete machen. Weitere Ziele wären: möglichst viel zur Compile-Zeit (boost::is_same<A,B>) oder möglichst anfängersicher.
Eine realistische absolut häßliche Möglichkeit wäre es auch alle Class-Test-Routinen in die Basis-Klasse zu verlagern und dann bei Bedarf zu überschreiben.class BaseSkill { virtual bool hasASkill() { return false; } virtual bool hasBSkill() { return false; } } class ASkill : public BaseSkill { virtual bool hasASkill() { return true; } }Ob nun templatisiert oder nicht spielt für den eigentlichen C++-Business-Code keine direkte Rolle, ob nun
if(unit->has<ASkill>()) ... if(unit->hasASkill()) ...Deswegen bin ich für gute Ideen weitestgehend offen.
-
wxSkip schrieb:
Und was spricht gegen einen Referenzzähler?
-
Wie oft wird has<>() denn aufgerufen? Wenn ich das so sehe, scheint der vector quasi nie mehr als 100 Elemente haben. Das sollte ja trotz dynamic_cast recht flott gehen. Ich muss eine solche Schleife 1000000 mal durchlaufen für 5 Sekunden Laufzeit. (Und da wird jedes Mal bis 100 gerannt.)
Insofern wäre es vielleicht leichter die Zahl der Aufrufe zu minimieren. Ansonsten hier eine Idee Edit: Oh, ich glaube das hattest du schon vor. Der Vorschlag von Shade Of Mine scheint mir allerdings recht praktisch zu sein.
-
Was spricht denn jetzt gegen meinen Vorschlag von Seite 2? Die has<...> Funktion wird vom Aufwand her auf ein array indexing reduziert. Kann mir nicht vorstellen, dass es irgendwie schneller geht.
-
GorbGorb schrieb:
Was spricht denn jetzt gegen meinen Vorschlag von Seite 2? Die has<...> Funktion wird vom Aufwand her auf ein array indexing reduziert. Kann mir nicht vorstellen, dass es irgendwie schneller geht.
Oh, da habe ich ja schon einen Referenzzähler übersehen

-
GorbGorb schrieb:
Was spricht denn jetzt gegen meinen Vorschlag von Seite 2?
Gefällt mir sehr gut! - Ich glaub das könnte funktionieren.