Templates mit Basisklassen



  • Gibt es eine Möglichkeit, sowas zu realisieren?

    Beispiel:

    class dummy{
    };
    class dummy2:public dummy
    };
    
    template<class T>class dummies{
        T mydummy;
    };
    

    nun soll das ganze so gestaltet werden, dass nur templates für dummy und davon abgeleitete Klassen gültig sind.

    Habt ihr dazu irgendwelche Vorschläge?

    MfG PMrogan

    [ Dieser Beitrag wurde am 05.04.2003 um 21:03 Uhr von PMrogan editiert. ]



  • nun soll das ganze so gestaltet werden, dass nur templates für dummy und davon abgeleitete Klassen gültig sind.

    Wie wär's mit sowas:

    template <bool> class CompileTimeAssert;
    template <> class CompileTimeAssert<true> {};
    
    template <class T, class U>
    struct IsDerived
    {
        typedef char no;
        struct yes {char d[2];};
        static yes check(const volatile U*);
        static no check(...);
        enum {value = sizeof( check((T*)0)) == sizeof(yes)};
    };
    
    class dummy
    {};
    class dummy2:public dummy
    {};
    
    template<class T>
    class dummies
    {
        public:
           T mydummy;
           ~dummies()        
           {
               CompileTimeAssert<IsDerived<T,dummy>::value> test;
               (void) test;
           }
    
    };
    
    int main()
    {
        dummies<dummy> a;  // ok
        dummies<dummy2> b;  // ok
        dummies<int> c;    // error;
    }
    


  • woher hast du das? sieht 🕶 aus 🙂 und ich versteh's sogar 😃



  • Hm, ich kann damit nix anfangen.

    @Hume -> kurze erklärung drinn ?

    thx



  • ich erklärs dir: geht mit funktion überladen:
    yes check (const volatile U ) und no check (...)
    beim Überprüfen in enum {value = sizeof(check((T
    )0)) == sizeof(yes)};
    sieht er nach, ob entweder die Funktion
    yes check (const volatile U ) aufgerufen wird, die nur dann genommen wird, wenn
    T von U eine Tochterklasse ist, weil nur dann (T
    )0 nach (U*)0 gecastet werden darf. sizeof(check(const volatile U*)) == 2, da check(const volatile U*) ein yes zurückgibt (2 chars).
    Wenn die andere Funktion genommen wird (no check(...)), und die wird in allen anderen Fällen, wenn T nicht von U erbt, genommen, dann ist sizeof == 1 und somit wird value false.
    Das ganze muss man dann nur noch in einer Funktion von der anderen Klasse üperprüfen, die sicher instanziiert wird: der Destruktor
    🙂



  • ich erklärs dir:

    Jo. Erklärung sieht doch gut aus. Nur ein paar kleine Hinweise und Ergänzungen habe ich noch:

    weil nur dann (T*)0 nach (U*)0 gecastet werden darf

    cast ist das falsche Wort. Konvertiert wäre richtig.

    sizeof(check(const volatile U*)) == 2

    Es muss nicht == 2 sein. Kann auch größer sein. Wichtig ist nur, dass sizeof(yes) *garantiert echt größer* als sizeof(no) ist.

    Und die Ergänzungen:
    Wie man sieht ist weder check(const volatile U*) noch check(...);
    definiert. Muss auch nicht, da keine der beiden Funktionen hier jemals aufgerufen wird. Bei der Auswertung des sizeof-Ausdrucks wird nur geprüft, welche Funktion aufgerufen werden *würde*. Ein Aufruf findet aber nicht statt.

    Soweit so klar. value ist also entweder 0 (falls check(...) ausgewählt wurde) oder 1 (falls check(const volatile U*)).
    Jetzt kommt meine zweite Anmerkung.
    Die Templateklasse CompileTimeAssert erwartet einen einzelnen bool non-type-Parameter. Sie kann also entweder mit true oder mit false instanziiert werden. Allerdings existiert nur für true eine Definition. Für false ist die Klasse lediglich *deklariert*.

    Im Destruktor von dummies wird jetzt ein Objekt der Templateklasse CompileTimeAssert angelegt. Als Wert für den non-type Parameter wird der zuvor bestimmte enum-Wert aus IsDerived genommen. Sollte dieser 0 (= false = nicht abgeleitet) sein, so wird versucht ein Objekt der *nicht kompletten* (da nur deklarierten) Klasse CompileTimeAssert<false> anzulgen. Und das ist nicht erlaubt -> Fehler. Die Anweisung
    (void) test;
    sorgt nur dafür, dass im true-Fall keine Warnung wegen eines unreferenzierten Objekts ausgegeben wird.

    woher hast du das?

    Aus dem Ärmel geschüttelt 😃
    Allerdings ist mir ähnlicher Code schon in Sutters "More Exceptional C++", im CUJ, in Vandervoordes und Josuttis' "C++ Templates - The Complete Guide" und in Alexandrescus Loki-Lib über den Weg gelaufen. Ich habe mir das also nicht selbst ausgedacht 🙂



  • DAS ist wohl der Grund, warum volkard von C++ abdriftet... (oder hab ich das falsch beobachtet?) Ich zumindest finde das nicht wirklich elegant.



  • ICH liebe sowas 😃 🙂 mmhm
    freilich hätte das irgendwie als sprachmittel eingebaut werden können, aber so entdeckt man doch immer was neues 🙂



  • @Mr. N
    Wieso. Nimm eine Bibliothek wie Loki oder Boost (sehr zu empfehlen, wenn man mit Templates arbeitet). Dann reduziert sich das ganze auf eine Zeile:

    template <class T>
    class X : IsDerived<T, Foo>
    {...};
    

    Oder ähnlich.

    Das Entscheidende ist doch, dass man solchen Code nur *einmal* schreibt und dann in Form einer Bibliothek nachnutzt.

    Ich muss sagen, dass ich das äußerst elegant finde.



  • Ich aber nicht. Solche fancy Sachen gehören in die Sprache.



  • Original erstellt von Mr. N:
    Ich aber nicht. Solche fancy Sachen gehören in die Sprache.

    Höchstens in die STL und da Teile von boost wohl sowieso in den nächsten C++-Standard wandern werden ist die Wahrscheinlichkeit dass sowas ähnliches dort landet vielleicht ja nichtmal so gering.



  • Da Prozessorzeit wertlos ist tun wirs halt mal in die STL. Das wird die Compilezeit von Dingen wie Mozilla bestimmt kaum verändern :o ...
    Davon abgesehen, dass die Implementierung das Gegenteil von hübsch ist.



  • Original erstellt von Mr. N:
    **Da Prozessorzeit wertlos ist tun wirs halt mal in die STL. Das wird die Compilezeit von Dingen wie Mozilla bestimmt kaum verändern :o ...

    Davon abgesehen, dass die Implementierung das Gegenteil von hübsch ist.**

    Was glaubst Du denn braucht Humes Hack großartig an CPU-Zeit? Lies Dir den Code doch mal genau durch!
    Und warum zum Geier soll das die Mozilla-Compilezeit in die Höhe treiben? 😕
    Was findest Du denn an der Implementierung so unhübsch? Sie ist gut verständlich und funktioniert völlig ohne Laufzeitkosten, was ist daran nicht gut?



  • Was glaubst Du denn braucht Humes Hack großartig an CPU-Zeit? Lies Dir den Code doch mal genau durch!
    Äh... der Compiler ist ja magisch...
    Und warum zum Geier soll das die Mozilla-Compilezeit in die Höhe treiben? 😕
    ... Wenn das in mehreren Header verwendet wird (Mozilla is C++), schließlich lassen sich templates nich exporten, dann sind das bei den paar wenigen source files ein bisschen was. Zugegeben keine Stunde :D.
    > Was findest Du denn an der Implementierung so unhübsch? Sie ist gut verständlich und funktioniert völlig ohne Laufzeitkosten, was ist daran nicht gut?
    GUT VERSTÄNDLICH? Gut verständlich heißt, beim ersten Anschauen verständlich. Dieser Hack mit sizeof und (T *)0 ist für meine Augen nicht sofort verständlich.



  • Original erstellt von Mr. N:
    *GUT VERSTÄNDLICH? Gut verständlich heißt, beim ersten Anschauen verständlich. Dieser Hack mit sizeof und (T )0 ist für meine Augen nicht sofort verständlich.

    Welches c++ Konstrukt ist den beim ersten mal verständlich?
    Ich finde das Template zeugs ist eine zweite Sprache, die man seperat lernen kann wenn man es will.

    Für jemand der mal Modern C++ Desing gelesen hat ist das hier doch ein klacks. Verzweifle nicht wenn du die ganzen Keywörter kennst aber nicht weist was sie in diesen Zusammenhang bewirken, es sind nur ein paar neue Sachen + den Skill duch die <> duch zu blicken.

    Zu der Sache das es von der Sprache direkt unterstützt werden sollte:
    Ein paar Nachteile:
    Die Sparche wird immer dicker und unlenbar.
    So eine lib wird nie alles können was du brauchst, dann muss du wieder zu den Hacks greifen.
    volkard meinte mal das er sich eine Sprache wüncht die nur goto kann, while for und co. gibts implementiert mit hilfe von goto in einen Modul der Standard Lib

    Trift dieses hier nicht genau den Gedanken?

    Zu der Sache mit der Compilierzeit:
    lohnt es sich dafür c++ umzukrämpeln?,
    es steckt noch ne menge potenzial in den compiliern
    jeden monat gibt es ein cpu der 100 mhz mehr hat

    *wieder mal c++ schön geredet* 😉

    [ Dieser Beitrag wurde am 06.04.2003 um 04:35 Uhr von Dimah editiert. ]



  • OK, Danke!

    Werds mal implementieren...

    MfG PMrogan



  • Nachdem sich meine Problemstellung geringfügig geändert hatte,
    musste ich das ganze noch etwas modifizieren:

    Nun werden nur abgeleitete Klassen akzeptiert:

    template <class T, class U>
    struct IsDerived
    {
        typedef char no;
        struct yes {char d[2];};
        static yes checkD(const volatile U*); //Is derived?
        static no checkD(...);
        static yes checkB(const volatile T*); //Is base class?
        static no checkB(...);
        enum {value = sizeof( checkD((T*)0)) == sizeof(yes) && sizeof( checkB((U*)0)) == sizeof(no)};
    };
    

    MfG PMrogan



  • @Mr.N
    Irgendwie ist mir deine Aggressivität nicht ganz klar. Wie auch immer. Hier meine 0.02 Euro:

    Ich aber nicht.

    Warum du es nicht ellegant findest, hast du noch nicht beantwortet. Ist aber auch nicht so wichtig.

    Solche fancy Sachen gehören in die Sprache.

    Das halte ich für nichts sagend. Definiere "fancy Sachen". Was für dich fancy ist, mag für mich trivial sein und andersherum. Da musst du schon konkreter weredn. Und selbst wenn wir dann wissen was du mit "fancy Sachen" meinst. Wo willst du aufhören. Was soll in die Sprache und was nicht? Der Stroustrup hat mittlerweile mehr als 1000 Seiten. Wie groß soll er noch werden?

    Wenn du damit meinst, dass sowas wie ein Type-Traits-System (oder mindestens ein typeof) in die Sprache soll, ok damit könnte ich mich auch anfreunden. Aber alle Tempalate-Tricks von z.B. boost in die Sprache integrieren? Das kann wirklich nicht dein ernst sein.

    GUT VERSTÄNDLICH? Gut verständlich heißt, beim ersten Anschauen verständlich

    Gust verständlich ist einzig eine Frage der Ausbildung. Wenn jedes zweite C++ Buch einem Anfänger veraltetes kaputtes Pre-Standard-C++ beibringt, ist es kein wunder, dass für die Leser solcher Bücher Templatecode oder die STL nicht gut verständlich ist. Fängst du allerdings z.B. mit "Accelerated C++" an, dann sieht das schon etwas anders aus. Sicher, Templates werden in C++ sicher nie den "schöne-Syntax-Preis"-Gewinnen, dazu sind sie zu spät in die Sprache integriert wurden, Templatecode aber als generell unverständlich zu bezeichnen halte ich für Ignoranz.

    Templatecode wird mit der Komplexität der zu lösenden Aufgabe immer schwieriger. Vergleicht man aber mal die Komplexität der hier gestellten Aufgabe mit der Komplexität der Lösung (nimm noch eine Bibliothek hinzu), dann finde ich das Verhältnis noch sehr erträglich.

    Äh... der Compiler ist ja magisch...

    Nein. Natürlich nicht. Nur steht es zu erwarten, dass Compiler die Optimierungen die sie an anderen Stellen schon seit Jahrhunderten benutzen irgendwann auch in der Templategenerierung einsetzen werden. Zumindest wenn Templates in C++ weiter so beliebt bleiben.

    Für mich ist das mit den Templates ein bischen wie mit der OOP. Derjenige der die krassen Bibliotheken schreibt, muss mit einer großen Komplexität kämpfen. Anwender einer Bibliothek erhalten aber sehr mächtige Werkzeuge die dennoch relativ leicht zu verwenden sind.

    [ Dieser Beitrag wurde am 06.04.2003 um 11:55 Uhr von HumeSikkins editiert. ]



  • templates selber verstehe ich ja. verstehen kann ich

    template <bool> class CompileTimeAssert;
    template <> class CompileTimeAssert<true> {};
    
    template <class T, class U>
    struct IsDerived
    {
        typedef char no;
        struct yes {char d[2];};
        static yes check(const volatile U*);
        static no check(...);
        enum {value = sizeof( check((T*)0)) == sizeof(yes)};
    };
    

    auch. aber erst nachdem ich 5 minuten darüber nachgedacht habe. typkompatibilitätskeywords auf der anderen seite fände ich nicht zuviel verlangt:

    X sametype Y // X und Y sind genau der gleiche typ
    X derivtype Y // X ist ein von Y abgeleiteter typ
    

    ist finde ich besser verständlich

    ps: kA warum ich manchmal so aggressiv bin



  • typkompatibilitätskeywords auf der anderen seite fände ich nicht zuviel verlangt:

    Das wäre in meine Augen zu speziell. Es gibt nahezu unendlich viele Fragen die man stellen kann bzw. Relationen in denen zwei Typen stehen können.

    Ein Type-Traits-Geschichte fest in die Sprache integriert hingegen fände ich wie gesagt keine schlechte Idee. Damit könnte man sich die komplexen Sachen dann leicht selbst basteln. Man wäre nicht so festgelegt wie bei neuen Keywords.


Anmelden zum Antworten