Wie kann man Code Kopie durch Template ersetzen?



  • 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.
    Optimalerweise

    MyClass<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);
    }
    


  • Und dann kann man dem Benutzer ja trotzdem noch die ersten 512 schenken.
    Aber alles, was drüber ist, muss er hübsch selber registrieren.

    template<unsigned N>
    void preRegister(){
        A<N> a;
        registerVtblAndFunction(a);
        B<N> b;
        registerVtblAndFunction(b);
        preRegister<N-1>();
    }
    template<>
    void preRegister<0>(){
        A<0> a;
        registerVtblAndFunction(a);
        B<0> b;
        registerVtblAndFunction(b);
    }
    
    int main()
    {
        preRegister<512>();
    
        A<13> a_13;
        A<25> a_25;
        A<70> a_70;
    
        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_25);
        obj_list.push_back(&a_70);
    
        MyClass myclass;
        myclass.func(obj_list);
    }
    


  • Das sind ja ganz nette Tricks (auch wenn man die eigentlich nicht benutzen müssen will), aber ich bin mir ziemlich sicher, dass das vergebene Liebesmüh ist. Der TE hat doch nie im Leben ein echtes Programm vor sich, in dem Klassen bzw. Klassenvorlagen namens "Base", "A" und "B" benutzt werden. Mit großer Wahrscheinlichkeit wurden da wichtige Teile des tatsächlich vorhandenen APIs rausgekürzt, die das Problem einfach zu behandeln machen.


Anmelden zum Antworten