Type ID mal Abartig anders. Meinungen dazu.



  • Thema:
    TypeId mal anders.
    Was ist eure Meinung?

    Gegeben ist:

    class Base;
    class A : public Base
    class B : public Base
    
    cBase *p = ?;
    

    Ja Tausendfach durchgekaut ich weis. 🙂

    Hier mal ein Abartiger Vorschlag:

    Gut ich habe die Klasse Base, die weis nichts von den anderen Klassen.
    Man könnte jetzt rtti einsetzen aber wie man hier im Forum lernen kann
    ist das sehr verpönnt. (Geschwindigkeit usw.)

    Vielleicht könnte man über eine Virtuelle Funktion eine Nummer zurück
    geben lassen. Nur wer Handhabt das mit den Nummern? Dazu müsste man wissen
    wie viele Klassen von der Base abgeleitet sind. Auch schlecht. Virtuelle
    Funktionen fallen eigentlich auch aus, da Base nichts weis von den anderen
    Klassen. Wer weis eigentlich das ein Objekt von Base abgeleitet ist?
    Eigentlich nur das Objekt selber. Wie könnte man das benutzen?

    Nase reib ... Funken sprühen .... und die Idee ist da.

    Hier meine Idee.
    Wir geben der Base Klasse eine leere Template Funktion. Diese dient
    dann als Reference.

    class cBase
    {
        template< typename type > void refFunc() {}
    };
    

    Jetzt gibt es eine virtuelle Funktion die ein Funktionspointer
    zurück gibt.

    class cBase
    {
        typedef void (cBase::*tRefFunc)();
    
        virtual tRefFunc getRefFunc() const { return cBase::refFunc<cBase>; }
    
        template< typename type > void refFunc() {}
    };
    

    Und als Letztes gibt es eine Template Funktion die zurück gibt
    ob es Klasse X oder Klasse Y ist, oder doch nur die Base.

    class cBase
    {
        template< typename type > bool bistDuEs() const;
    };
    

    Dies Funktion hollt sich jetzt über die virtuelle Funktion "getRefFunc"
    den Funktionspointer und vergleicht Ihn mit der, den Type entsprechenden
    refFunc. An besten ich zeig es mal.

    typedef void (cBase::*tRefFunc)();
    template< typename type > bool bistDuEs() const
    {
        tRefFunc RefFunc_Soll = &cBase::refFunc<type>;
        tRefFunc RefFunc_Ist = getRefFunc();
        return RefFunc_Soll == RefFunc_Ist;
    }
    

    Was muss das Objekt machen das von Base abgeleitet ist. Eigentlich
    nur die virtuelle Funktion getRefFunc überladen und den Pointer auf
    die refFunc der Base mit dem Template Parameter this als Rückgabewert
    zurückliefern.

    class A : public cBase
    {
        virtual tRefFunc getRefFunc() const { return cBase::refFunc<A>; }
    };
    

    Benutzen wird nun mal die Funktion "bisDuEs" in einen Beispiel.

    class A : public cBase;
    class B : public cBase;
    class cBase *p = A;
    
    p->bistDuEs< A >()
        {
            // hier holt sich die Funktion wie gefordert den
            // pointer der Reference Funktion mit Template Parameter
            // Klasse A
            tRefFunc RefFunc_Soll = &cBase::refFunc< A >;
    
            // hier kommt der Pointer von der "getRefFunc"
            // Funktion. In unseren Fall ist dies von Klasse A
            // Überladen und liefert seinerseits den Pointer
            // von refFunc< A > zurück.
            tRefFunc RefFunc_Ist = getRefFunc();
    
            // Die beiden Pointer sind also gleich, deshalb gibt die
            // Funktion true zurück.
            return RefFunc_Soll == RefFunc_Ist;
        }
    
    p->bistDuEs< B >()
        {
            // hier holt sich die Funktion wie gefordert den
            // pointer der Reference Funktion mit Template Parameter
            // Klasse A
            tRefFunc RefFunc_Soll = &cBase::refFunc< A >;
    
            // hier kommt der Pointer von der "getRefFunc"
            // Funktion. In unseren Fall ist dies von Klasse B
            // Überladen und liefert seinerseits den Pointer
            // von refFunc< B > zurück.
            tRefFunc RefFunc_Ist = getRefFunc();
    
            // Die beiden Pointer sind also ungleich, deshalb gibt die
            // Funktion false zurück.
            return RefFunc_Soll == RefFunc_Ist;
        }
    

    So sieht doch gut aus.

    WARNUNG:
    Dieses Funktioniert nur wenn es nur jeweils eine Codestelle der
    Funktion "refFunc< type >" im Programm gibt.

    Naja, vielleicht nicht ganz Praxis Tauglich, aber was meint Ihr dazu?

    Lichtlein


  • Administrator

    Lichtlein schrieb:

    Gut ich habe die Klasse Base, die weis nichts von den anderen Klassen.
    Man könnte jetzt rtti einsetzen aber wie man hier im Forum lernen kann
    ist das sehr verpönnt. (Geschwindigkeit usw.)

    Du meinst wohl eher dynamic_cast ? RTTI ist einfach nur Runtime Type Information. RTTI kannst du auch mit dem Schlüsselwort typeid verwenden:
    http://www.cplusplus.com/reference/std/typeinfo/type_info/

    Das entspricht in etwa dem, was du hier machst.
    Es ist übrigens nicht einfach verpönt, sondern wie schon vielfach gesagt, ein Indiz für schlechtes Design, wenn man den dynamic_cast benötigt.

    Grüssli



  • Hallo, ich wollte eigentlich keine Diskusion über Dynamic Cast oder so, sondern es ging mir um die Idee, die Automatische Erzeugung von Funktionen durch den Compiler zu benutzen. (Template Code)

    Es ist mehr eine "Funktioniert das so" Studie. Seht es einfach nur als Anregung. 🙂

    Lichtlein


  • Administrator

    Lichtlein schrieb:

    Seht es einfach nur als Anregung. 🙂

    Anregung wofür? Es gibt ja bereits typeid . Sieh dir den Link an.

    Oder willst du einfach nur wissen, ob dein Code funktionieren würde? Gründsätzlich wohl schon, aber ich würde empfehlen die Funktion "bistDuEs" als freie Funktion auszulagern. Als freie Funktion kannst du dann zwei anbieten:

    • is_type<T>(Base& base)
    • is_same_type(Base& lhs, Base& rhs)

    Auch würde ich das typedef anders nennen, nämlich zum Beispiel type_id 😉

    Grüssli



  • Probiers aus, dann siehste du ob es geht (sieht auf den ersten Blick so aus).
    Allerdings gibt dir ein dynamic_cast die gleiche Anwort auf deine Frage. Dein Design wird nicht schöner, wenn du ein Typeswitch mit etwas was ein dynamic_cast imitiert hast.
    Die Frage ist, wieso du überhaupt wissen musst ob es A oder B ist. Der Sinn der Ploymorphie ist ja gerade nicht wissen zu müssen ob es A oder B ist, bzw. gar nicht wissen zu müssen was A und B ist, sondern nur eine Schnittstelle zu kennen.



  • @Dravere
    Gute Idee mit dem Auslagern. Ist dann auch Flexibler und nicht so Classe Base abhänig.

    Das Prinzip Funktioniert, das habe ich schon ausprobiert.

    Experimentiere hier ein wenig mit "Automatisch-Funktionalen" Aspekten in C++ herum. Das mit der TypeId ist nur ein kleines Nebenprodukt meiner Überlegungen, nicht Praxis Relevant (Hatte ich aber schon erwähnt).



  • Hallo

    Als ID-Generator finde ich die Idee des Methoden-Pointers generell recht gut (ich mag ja auch das safe-bool Idiom sehr :D).

    Aber zur Implementierung hätte ich schon eine Frage:
    Bei der Optimierung des erzeugten Codes könnte der Compiler feststellen, dass alle "void refFunc<T>()" Methoden nichts tun und eigentlich unabhängig vom template-Parameter ist. Kann man also verhindern, dass 5 refFunc<T> - Methoden dann nicht auf einen einzigen Code-Block zeigen, der nur ein "return" beinhaltet ?
    Eventuell vorsichtig müsste man beim Portieren sein. Es gibt Compiler (damit mein ich vor allem den OpenWatcom), die die Form &Class::Method<T> nicht verstehen können (leider 😞 ).

    Ein anderer Punkt ist die ID selbst, die bei jedem Kompiliervorgang dann anders aussehen kann. Die Praxis hat mir gezeigt, dass beim Bedarf einer ID für eine Klasse sich diese nicht mehr ändern sollte (zwecks Serialisierung ...). Ist aber hier nur ein Kommentar am Rande 😃

    Ich selbst habe bei der ID-Frage schon allerlei Unsinn mit Makros, die den Klassennamen an einen Member hängen, angestellt. Leider sind string-Konstanten-Pointer absolut nicht eindeutig.
    Eines muss ich allerdings gestehen: Bei allen Versuchen dynamic_cast und RTTI abzulösen habe ich immer nur schlechtere bzw. schwächere Lösungen gefunden, als die, die vom Compiler erzeugt wurden. Wenn man sich Exceptions, Mehrfachvererbung und die Verkettung von Klassen ansieht, kommt man um den geringen Overhead von RTTI einfach nicht herum

    lg XOR



  • Die Adresse einer statischen Membervariable zurückzugeben wäre IMO viel einfacher, und erfüllt genau den selben Zweck.



  • @xor

    Der Compiler darf das gar nie nicht! :-). Kann er auch nicht. Wenn dann müste der Linker dieses machen. (Der weis aber noch weniger über den Code als der Compiler)

    Für Serialisierung taugt das ganze nichts. Da hast Du schon recht, da gibt es bessere möglichkeiten.

    Noch mal was zu dynamic_cast, rtti, Exceptions usw.
    Was hier in den Diskusionen leider immer wieder vergessen wird, ist das es nicht immer zur verfügung steht.

    Einfach mal den gcc mit -nostdlib aufrufen... schon war es das mit den ganzen netten C++ zusätzen.

    Lichtlein


Anmelden zum Antworten