Wie kann man Code Kopie durch Template ersetzen?
-
BöserBube schrieb:
volkard schrieb:
Übungsaufgabe vom Prof? Dann fehlt mehr Kontext, um zu ahnen, auf was er hinauswill.
Wenns von einem Prof käme könnte der vielleicht einen vptr-Hack erwarten. Standardkonform geht das jedenfalls nur mit den 2^17 ifs (weil unsigned nur von 0 bis 2^16 gehen muss).
@tjroamer: Wenn du wenigstens die Signatur in
std::vector<std::shared_ptr<thing_dispatcher*>>ändern könntest, hättest du noch eine kleine Chance (sofern du die Erstellung der Liste beeinflussen kannst).Es sieht so aus, dass ich 2^32 Mal schreiben muss, um alle möglichen Fälle abzudecken.
Ach nein, da ist ein Troll am Werk. Hat sich erledigt.
ich denke, ich muss aufgeben, der Parameter std::vector<Base*> ist leider nicht änderbar. Vielen Dank an euch allen für eure Zeit und gute Nacht!
Gruß,
Kai
-
volkard schrieb:
tjroamer schrieb:
Sone schrieb:
volkard schrieb:
tjroamer schrieb:
Die Anforderung ist: meine Klasse muss die public Funktion
Bitte die vollständige Anforderung schreiben. Immer wieder einen halben Satz zu kriegen, der alles umwirft, das ist nicht fein.
Wovon redest du?
tjroamer schrieb:
Die Anforderung ist: meine Klasse muss die public Funktion [...] func(std::vector<Base*> &obj_list)[...] anbieten, um andere sie aufrufen zu können.
Sorry, vielleicht habe ich nicht klar geschrieben. Jetzt ist die konkrete Anforderung:
**Meine Klasse muss den Usern die Funktion
void func(std::vector<Base*> *obj_list);zur Verfügung stellen. Die Funktion muss je nach der Subklasse do_thing_1() oder do_thing_2() richtig aufrufen.
**Gruß,
Kai
Süßer, wir haben den Code von Base immer noch nicht. Sind wenigstens do_thing_1 und do_thing_2 dort virtuell? Dann würde doch RTTI reichen.
die Klasse Base und A<N>, B<N> sehen so aus:
class Base { public: Base() {} virtual const char* kind(); virtual const char* name(); virtual ~Base(); }; template<unsigned int N> class A: public Base { public: A() {} void do_thing_1() { // do thing_1 depending on the N } virtual const char* kind() { return "A"; } virtual const char* name() { return "A"; } }; template<unsigned int N> class B: public Base { public: B() {} void do_thing_2() { // do thing_2 depending on the N } virtual const char* kind() { return "B"; } virtual const char* name() { return "B"; } };do_thing_1() und do_thing_2() sind keine virtual

-
Aber Hase! Da sind ja weder do_thing_1 oder ~_2 in der Basisklasse deklariert, geschweige denn virtuell!
Übrigens, die sind so klein? In dem Fall sei doch ein ganz Böser Bezlebub' und schreib die zwei Zeilen da rein - ich nehme die Verantwortung auf mich:
virtual void do_thing_1() = 0; virtual void do_thing_2() = 0;
-
Sone schrieb:
Aber Hase! Da sind ja weder do_thing_1 oder ~_2 in der Basisklasse deklariert, geschweige denn virtuell!
Übrigens, die sind so klein? In dem Fall sei doch ein ganz Böser Bezlebub' und schreib die zwei Zeilen da rein - ich nehme die Verantwortung auf mich:
virtual void do_thing_1() = 0; virtual void do_thing_2() = 0;anscheinend gibt es keinen anderen Weg. OK, ich versuche auf diesen Weg zu gehen. Vielen Dank.
Gruß,
Kai
-
tjroamer schrieb:
Das ist fast unmöglich, 2^32 Möglichkeiten aufzulisten.
Das ist nicht dein Problem. Dein Problem ist, dass du 2^32+ verschiedene Funktionen hast.
-
camper schrieb:
tjroamer schrieb:
Das ist fast unmöglich, 2^32 Möglichkeiten aufzulisten.
Das ist nicht dein Problem. Dein Problem ist, dass du 2^32+ verschiedene Funktionen hast.
Fast ein wenig ironisch - Stroustrup schreibt noch, Templates sind gut, um den Maschinencode zu verkürzen

Edit: Oder so ähnlich.
-
camper schrieb:
tjroamer schrieb:
Das ist fast unmöglich, 2^32 Möglichkeiten aufzulisten.
Das ist nicht dein Problem. Dein Problem ist, dass du 2^32+ verschiedene Funktionen hast.
die Funktion operate() ist als template definiert. Hier ist kein Problem. Der User kann folgenden Code schreiben:
A<13> a_13(); B<1000> b_1000(); std::vector<Base*> obj_list; obj_list.push_back(&a_13); obj_list.push_back(&b_1000); MyClass myclass(); myclass.func(obj_list);MyClass muss solchen Code beherrschen. Das ist mein Problem.
Gruß,
/Kai
-
A<13> a_13();//er wird B<1000> b_1000();//sich wundern std::vector<Base*> obj_list; obj_list.push_back(&a_13);//hier obj_list.push_back(&b_1000); MyClass myclass(); myclass.func(obj_list);
-
volkard schrieb:
A<13> a_13();//er wird B<1000> b_1000();//sich wundern std::vector<Base*> obj_list; obj_list.push_back(&a_13);//hier obj_list.push_back(&b_1000); MyClass myclass(); myclass.func(obj_list);gibt es hier einen Tippfehler oder wie?
Gruß,
Kai
-
Ich sehe es sofort. :stolz:

Edit:
tjroamer schrieb:
volkard schrieb:
A<13> a_13();//er wird B<1000> b_1000();//sich wundern std::vector<Base*> obj_list; obj_list.push_back(&a_13);//hier obj_list.push_back(&b_1000); MyClass myclass(); myclass.func(obj_list);gibt es hier einen Tippfehler oder wie?
Das sind Funktionsdeklarationen.
Guckstdu.
-
Sone schrieb:
Ich sehe es sofort. :stolz:

Edit:
tjroamer schrieb:
volkard schrieb:
A<13> a_13();//er wird B<1000> b_1000();//sich wundern std::vector<Base*> obj_list; obj_list.push_back(&a_13);//hier obj_list.push_back(&b_1000); MyClass myclass(); myclass.func(obj_list);gibt es hier einen Tippfehler oder wie?
Das sind Funktionsdeklarationen.
Guckstdu.OK. Wahrscheinlich habe ich zu einfach geschrieben. Ich wollte schreiben, a_13 und b_1000 sind zwei Klassenobjekten.
sollte sein:
A<13> a_13; B<1000> b_1000;Gruß,
/Kai
-
tjroamer schrieb:
camper schrieb:
tjroamer schrieb:
Das ist fast unmöglich, 2^32 Möglichkeiten aufzulisten.
Das ist nicht dein Problem. Dein Problem ist, dass du 2^32+ verschiedene Funktionen hast.
die Funktion operate() ist als template definiert. Hier ist kein Problem. Der User kann folgenden Code schreiben:
A<13> a_13(); B<1000> b_1000(); std::vector<Base*> obj_list; obj_list.push_back(&a_13); obj_list.push_back(&b_1000); MyClass myclass(); myclass.func(obj_list);MyClass muss solchen Code beherrschen. Das ist mein Problem.
Gruß,
/Kai
Aber der User ist bei Euch und kann auch angewiesen werden, die Liste ein wenig anders zu bauen?
Zum Beispiel ist die Liste ein Template-Listen-Ding, was die Typen der Elemente alle kennt? //Am besten, aber für Dich am schwierigsten und schränkt die Benutzer am meisten ein.
Oder dürfen wir einen Wrapper um A und B bauen, der zusätzlich die Adresse der wichtigen Funktion kennt und nur solche Objekte darf der User anlegen? //Naja, häßlich.
Oder auch nur den Zwang, wenn er ein A<13> benutzt, muß er auch
registerVptrAndFunc<A<13>>();mindestens einmal im Programm aufrufen? //Stört den Benutzer vermutlich am wenigsten und ist auch schnell gebastelt. Leider compilerabhängige Tricks.
-
tjroamer schrieb:
die Funktion operate() ist als template definiert.
Um das Template geht es nicht. Sondern um die daraus instantiierten Funktionen.
Ich hab grad mal im Anhang B (N3337) nachgeguckt - komisch. Sollte doch irgendwie impliziert sein, dass....
-
Wir gehen davon aus, daß die Benutzer nur ein paar, vielleicht mal 50 A- und B-Klassen in einem Projekt benutzt, das sind dann 100 Funktionen. Ok, wenn er sie braucht. Der Compiler erzeugt sie.
Aber wenn Du anmeldest, 2^32 Funktionen zu brauchen, indem Du in lauter if-Zeigen eine der Adressen anfragst, machste die ausführbarre Datei recht groß.
Wäre die erste 400GB große exe-Datei, die ich sehen würde.
Mach mal.
-
volkard schrieb:
Wir gehen davon aus, daß die Benutzer nur ein paar, vielleicht mal 50 A- und B-Klassen in einem Projekt benutzt, das sind dann 100 Funktionen. Ok, wenn er sie braucht. Der Compiler erzeugt sie.
Aber wenn Du anmeldest, 2^32 Funktionen zu brauchen, indem Du in lauter if-Zeigen eine der Adressen anfragst, machste die ausführbarre Datei recht groß.
Wäre die erste 400GB große exe-Datei, die ich sehen würde.
Mach mal.Ja, der Nutzer verwendet nur normalerweise nur bis zur 512 . Ich kann 512*2 if-anweisung manuell schreiben. Ich habe gefragt weil ich dumm fand wenn man solchen hässlichen code schreiben muss. Aber man muss so tun wenn nichts andere geht.
-
volkard schrieb:
Wir gehen davon aus, daß die Benutzer nur ein paar, vielleicht mal 50 A- und B-Klassen in einem Projekt benutzt, das sind dann 100 Funktionen. Ok, wenn er sie braucht. Der Compiler erzeugt sie.
Aber wenn Du anmeldest, 2^32 Funktionen zu brauchen, indem Du in lauter if-Zeigen eine der Adressen anfragst, machste die ausführbarre Datei recht groß.
Wäre die erste 400GB große exe-Datei, die ich sehen würde.
Mach mal.Mach mal.
Ja, der Nutzer verwendet nur normalerweise nur bis zur 512 . Ich kann 512*2 if-anweisung manuell schreiben. Ich habe gefragt weil ich dumm fand wenn man solchen hässlichen code schreiben muss. Aber man muss so tun wenn nichts andere geht.
-
tjroamer schrieb:
Ja, der Nutzer verwendet nur normalerweise nur bis zur 512 . Ich kann 512*2 if-anweisung manuell schreiben. Ich habe gefragt weil ich dumm fand wenn man solchen hässlichen code schreiben muss. Aber man muss so tun wenn nichts andere geht.
512 geht. Da explodiert das Programm noch nicht.
OptimalerweiseMyClass<400,150> myclass; myclass.func(obj_list);wobei der Benutzer mit 400 und 150 die höchste verwendete A-Nummer und B-Nummer angibt. Und wenn er sie wegläßt, sind's halt 512. Und wenn er nur die B-Nummer wegläßt, ist sie gleich der A-Nummer.
Die von func pro Objekt aufgerufene Funktion würde dann anhand von ->name() nach A oder B spalten und dann die jeweilige rekursive Template-Funktion heraufrutschen und bei Treffer die Nutzfunktion aufrufen und das Rutschen abbrechen.
Hab gerade leider keine Zeit, es einzutippen.
-
#include <iostream> #include <vector> #include <cstring> class Base { public: Base() {} virtual ~Base() {}; virtual const char* kind()=0; virtual const char* name()=0; }; template<unsigned int N> class A: public Base { public: A() {} void do_thing_1() { std::cout<<"A<"<<N<<">\n"; } virtual const char* kind() { return "A"; } virtual const char* name() { return "A"; } }; template<unsigned int N> class B: public Base { public: B() {} ~B() {} void do_thing_2() { std::cout<<"B<"<<N<<">\n"; } virtual const char* kind() { return "B"; } virtual const char* name() { return "B"; } }; template<unsigned int N> void do_thing_A(Base* base){ if(A<N>* a=dynamic_cast<A<N>*>(base)) a->do_thing_1(); else do_thing_A<N-1>(base); }; template<> void do_thing_A<0>(Base* base){ if(A<0>* a=dynamic_cast<A<0>*>(base)) a->do_thing_1(); else std::cerr<<"MAXA too low\n"; }; template<unsigned int N> void do_thing_B(Base* base){ if(B<N>* a=dynamic_cast<B<N>*>(base)) a->do_thing_2(); else do_thing_B<N-1>(base); }; template<> void do_thing_B<0>(Base* base){ if(B<0>* a=dynamic_cast<B<0>*>(base)) a->do_thing_2(); else std::cerr<<"MAXB too low\n"; }; template<unsigned int MAXA=512,unsigned int MAXB=MAXA> class MyClass { public: void func(std::vector<Base*> &obj_list) { for (std::vector<Base*>::iterator it=obj_list.begin(); it!=obj_list.end(); ++it){ Base* base=*it; if(strcmp(base->name(),"A")==0) do_thing_A<MAXA>(base); if(strcmp(base->name(),"B")==0) do_thing_B<MAXB>(base); } } }; int main() { A<13> a_13; B<100> b_100; std::vector<Base*> obj_list; obj_list.push_back(&a_13); obj_list.push_back(&b_100); MyClass<512,512> myclass; myclass.func(obj_list); }
-
Nicht schön, aber bläht die exe nicht so auf und läuft auch schneller.
#include <iostream> #include <vector> #include <cstring> #include <unordered_map> #include <typeinfo> class Base { public: Base() {} virtual ~Base() {}; virtual const char* kind()=0; virtual const char* name()=0; }; template<unsigned int N> class A: public Base { public: A() {} void do_thing_1() { std::cout<<"A<"<<N<<">\n"; } virtual const char* kind() { return "A"; } virtual const char* name() { return "A"; } }; template<unsigned int N> class B: public Base { public: B() {} ~B() {} void do_thing_2() { std::cout<<"B<"<<N<<">\n"; } virtual const char* kind() { return "B"; } virtual const char* name() { return "B"; } }; std::unordered_map<std::type_info const*,void(*)(Base*)> theMap; template<unsigned N> void do_func_a(Base* base){ static_cast<A<N>*>(base)->do_thing_1(); } template<unsigned N> void do_func_b(Base* base){ static_cast<B<N>*>(base)->do_thing_2(); } template<unsigned N> void registerVtblAndFunction(A<N>& a){ theMap[&typeid(A<N>)]=&do_func_a<N>; } template<unsigned N> void registerVtblAndFunction(B<N>& b){ theMap[&typeid(B<N>)]=&do_func_b<N>; } void do_func(Base* base){ auto it=theMap.find(&typeid(*base)); if(it!=theMap.end()) (*(it->second))(base); } class MyClass { public: void func(std::vector<Base*> &obj_list) { for (std::vector<Base*>::iterator it=obj_list.begin(); it!=obj_list.end(); ++it){ Base* base=*it; do_func(base); } } }; int main() { A<13> a_13; registerVtblAndFunction(a_13);//Einmal reicht A<13> a_13_2; A<13> a_13_3; B<1000000000> b_1000000000; registerVtblAndFunction(b_1000000000); std::vector<Base*> obj_list; obj_list.push_back(&a_13); obj_list.push_back(&b_1000000000); obj_list.push_back(&a_13_2); obj_list.push_back(&a_13_3); registerVtblAndFunction(a_13);//Mehrmals schadet nicht MyClass myclass; myclass.func(obj_list); }
-
Und das ist die Version, wenn man den vptr illegalerweise ausspäht. Ich weiß nicht, wie es heute ist, aber früher war alles mit RTTI unglaublich bloatig und lahm.
#include <iostream> #include <vector> #include <cstring> #include <unordered_map> #include <typeinfo> class Base { public: Base() {} virtual ~Base() {}; virtual const char* kind()=0; virtual const char* name()=0; }; template<unsigned int N> class A: public Base { public: A() {} void do_thing_1() { std::cout<<"A<"<<N<<">\n"; } virtual const char* kind() { return "A"; } virtual const char* name() { return "A"; } }; template<unsigned int N> class B: public Base { public: B() {} ~B() {} void do_thing_2() { std::cout<<"B<"<<N<<">\n"; } virtual const char* kind() { return "B"; } virtual const char* name() { return "B"; } }; std::unordered_map<char const*,void(*)(Base*)> theMap; char const* extractVptr(Base* b){ return *reinterpret_cast<char**>(b); } template<unsigned N> void do_func_a(Base* base){ static_cast<A<N>*>(base)->do_thing_1(); } template<unsigned N> void do_func_b(Base* base){ static_cast<B<N>*>(base)->do_thing_2(); } template<unsigned N> void registerVtblAndFunction(A<N>& a){ theMap[extractVptr(&a)]=&do_func_a<N>; } template<unsigned N> void registerVtblAndFunction(B<N>& b){ theMap[extractVptr(&b)]=&do_func_b<N>; } void do_func(Base* base){ auto it=theMap.find(extractVptr(base)); if(it!=theMap.end()) (*(it->second))(base); } class MyClass { public: void func(std::vector<Base*> &obj_list) { for (std::vector<Base*>::iterator it=obj_list.begin(); it!=obj_list.end(); ++it){ Base* base=*it; do_func(base); } } }; int main() { A<13> a_13; registerVtblAndFunction(a_13);//Einmal reicht A<13> a_13_2; A<13> a_13_3; B<1000000000> b_1000000000; registerVtblAndFunction(b_1000000000); std::vector<Base*> obj_list; obj_list.push_back(&a_13); obj_list.push_back(&b_1000000000); obj_list.push_back(&a_13_2); obj_list.push_back(&a_13_3); registerVtblAndFunction(a_13);//Mehrmals schadet nicht MyClass myclass; myclass.func(obj_list); }