Templates und Dateiaufteilung



  • gute frage, das frage ich mich auch schon die ganze zeit wo ich meine explizite spezialisierung à la

    template<> foo<int>(...)
    

    hinschreiben soll.



  • Sie sind ebenfalls nicht external linkage?

    Äh du mißverstehst da was. Templatefunktionen haben standardmäßig genauso external linkage wie alle anderen Funktionen auch. Im Gegensatz zu normalen Funktionen dürfen sie allerdings einmal pro ÜE definiert werden. Es ist Aufgabe des Compilers/Linkers dafür zu sorgen, dass am Ende nur eine Instanz übrig bleibt.

    Lars Hupel schrieb:

    gute frage, das frage ich mich auch schon die ganze zeit wo ich meine explizite spezialisierung à la

    template<> foo<int>(...)
    

    hinschreiben soll.

    Eine explizite Spezialisierung ist *kein* Template. Es gelten also die selben Regeln wie für normale Funktionen. Sprich: Wenn du sie im Header definieren willst, musst du sie inline deklarieren. Ansonsten deklarierst du sie im Header und definierst sie in einer cpp-Datei.



  • Aha, danke. Nun ist alles schon klarer geworden. 😉



  • das heißt also bei

    //main.h
    template<class T>bool foo(T var1,T var2)
    {
      return (var1==var2)
    
    template<> bool foo<char*>(char* var1,char* var2);
    
    //main.cpp
    template<> bool foo<char*>(char* var1,char* var2)
    {
      return strcmp(var1,var2)
    }
    

    macht der compiler was ich will, nämlich dass bei dem aufruf

    foo("a","b"); //1.
    foo<char*>("a","b"); //2.
    

    die explizierte spezialisierung aufruft, oder?



  • Sry aber das ist meiner meinung nach alles käse (mit inline und so...) das mit dem mehrfach includieren würd eh ned wirklich gehen... zumindest bei mir ned... dafür macht man ja:

    #ifndef _MY_HEADER_H_ //ist symbol definiert??
    #define _MY_HEADER_H_ //definiere symbol (nur für den kompiler bei compilieren)
    
    class ...
    {
    ...
    ...
    };
    
    #endif
    

    dann ist auch klar warum die template da stehen muss...
    die cpp-Source-Dateien werden ja vorcompiliert... nur bei templates weiß der kompiler ja ned welcher typ hingehört wenn er nur die SourceDatei compiliert...

    Kleines Beispiel:

    main.cpp //dein progi..
    TemplateClass.cpp //deine cpp SourceFile für klasse
    TemplateClass.h //deine header für klasse

    wenn jetzt der kompiler versucht TemplateClass.cpp vorzukompilieren wird er an deinen funktionen mit template scheitern da er ja ned weiß welcher typ dann im eigentlichen progi (main.cpp) verwendet wird.. klar?

    Die Lösung ist das du das dann eben ins Header Schreibst... das mit dem tl oder wie das war find ich ne gute Idee hab ich aber auch noch ned gehört da ich das eigentlich ned so oft brauch!

    Du kannst deine Sachen aber trotzdem schön auslagern in andere Dateien!

    Aja genau wollt noch sagen warums funktioniert wenns im header steht:
    Weil die header nicht kompiliert wird sondern in dein main.cpp eingebunden (wenn du sie inculdest) und dann weiß er ja welche funktionen er generieren muss anhand welcher aufrufe vorkommen und kann dies auch tun!

    Prinzipiel werden header so geschrieben das sie in einem Projekt nie öfter als 1 mal eingbunden werden im endeffekt!! das erreicht man eben durch dieses #ifndef _MY... usw.

    Ich hoffe es sind alle missverständnisse geklärt!!



  • Lars Hupel schrieb:

    die explizierte spezialisierung aufruft, oder?

    Ja, tut er. Aber das hättest du eigentlich auch schnell testen können?



  • sry hab grad keinen compiler zur hand, sonst hätte ich die frage ja nie gestellt



  • Manuelh87 schrieb:

    Sry aber das ist meiner meinung nach alles käse (mit inline und so...) das mit dem mehrfach includieren würd eh ned wirklich gehen... zumindest bei mir ned... dafür macht man ja:

    #ifndef _MY_HEADER_H_ //ist symbol definiert??
    #define _MY_HEADER_H_ //definiere symbol (nur für den kompiler bei compilieren)
    
    class ...
    {
    ...
    ...
    };
    
    #endif
    

    Include-Guards helfen dir hier relativ wenig. Es ist kein Compilerfehler, sondern ein Linkerfehler, der dabei entsteht. Include Guards verhindern nur das mehrfache Einbinden in EINER ÜE. Hier hast du aber mehrere. Das wird auch bei dir einen Fehler auslösen, da bin ich mir sicher. 😉

    dann ist auch klar warum die template da stehen muss...
    die cpp-Source-Dateien werden ja vorcompiliert... nur bei templates weiß der kompiler ja ned welcher typ hingehört wenn er nur die SourceDatei compiliert...

    Kleines Beispiel:

    main.cpp //dein progi..
    TemplateClass.cpp //deine cpp SourceFile für klasse
    TemplateClass.h //deine header für klasse

    wenn jetzt der kompiler versucht TemplateClass.cpp vorzukompilieren wird er an deinen funktionen mit template scheitern da er ja ned weiß welcher typ dann im eigentlichen progi (main.cpp) verwendet wird.. klar?

    Alle Templatedefinitonen stehen im Header, weil der Linker sonst die Definitionen in einer cpp-Datei, wo diese ausgelagert sind, nicht finden würde. Leider wird das export-Keyword von den Compilern noch nicht unterstützt, da es schwer zu implementieren ist (das sieht du ja daran, an dem was du gepostet hast). Dem Compiler ist das egal, er braucht nur eine Deklaration im Header, um zu sehen, dass es sie gibt. Er macht sich keine Sorgen mehr und überlässt das Zusammenbindem dem Linker, der natürlich Probleme macht, weil er keine generierte Version vom Template findet.

    Die Lösung ist das du das dann eben ins Header Schreibst... das mit dem tl oder wie das war find ich ne gute Idee hab ich aber auch noch ned gehört da ich das eigentlich ned so oft brauch!

    Du kannst deine Sachen aber trotzdem schön auslagern in andere Dateien!

    Aja genau wollt noch sagen warums funktioniert wenns im header steht:
    Weil die header nicht kompiliert wird sondern in dein main.cpp eingebunden (wenn du sie inculdest) und dann weiß er ja welche funktionen er generieren muss anhand welcher aufrufe vorkommen und kann dies auch tun!

    Genau. Und da die Leute vom C++ Comitee aufgepasst haben, sind Templates nur für eine ÜE sichtbar und der Linker kümmert sich darum, bloß eine Version davon zu bekommen. (anders wie bei Template Spezialisierungen, siehe hierzu HumeSikkins Beitrag weiter oben)

    Prinzipiel werden header so geschrieben das sie in einem Projekt nie öfter als 1 mal eingbunden werden im endeffekt!! das erreicht man eben durch dieses #ifndef _MY... usw.

    Eben nicht. Siehe oben.

    Ich hoffe es sind alle missverständnisse geklärt!!

    Ich denke, du bringst eher wieder neue hinein. 🙄



  • sry aber das kann ich mir ned vorstellen... du hast ja für alles schöne spezialausdrücke aber weißt du auch was du da redest?? Mehrfacheinbindungen machen prinzipiell würd ich mal sagen nen fehler:

    Die header werden ja erst compilierd wenn main.cpp compiliert wird (in meinem beispiel) und daher würds nen fehler geben wennst z.b. andre header einbindest wo deine header eingebunden ist weil dann ein name für z.b. ein klasse doppelt exisitieren würd besser mich aber bitte aus falls das nicht stimmt! Ich hab bis jetzt meine header immer mit "Code Guard" betrieben (sag ich mal) auch mit templates also versteh ich ned ganz was daran nicht richtig sein soll!

    (nur weil es ein intelligenter compiler vielleicht trotzdem checkt ists noch lang ned richtig! bestes beispiel sind viele java-scripts wo einfach am ende "}" zeichen weggelassen werden... funktioniert ist aber trotzdem falsch!!)

    das ganze ist ja ned so schwer:

    mann schreibts in nen header weil erst beim überstetzen einer cpp datei klar wird in welcher form die (z.b.) funktion gebraucht wird und dann wird sie erstellt! Glaub ned das sich daran was ändern wird und man jemals in ne cpp templates machen kann da der kompiler ja den (z.b.) type ned kennt! Das ist ne logische sache!

    oder seh ich da was falsch?
    na ich hoffe jetzt ist klar was ich mein!
    mfg Manuelh87



  • Manuelh87 schrieb:

    sry aber das kann ich mir ned vorstellen... du hast ja für alles schöne spezialausdrücke aber weißt du auch was du da redest??

    Ich hoffe schon, dass ich weiß, wovon ich rede.

    Mehrfacheinbindungen machen prinzipiell würd ich mal sagen nen fehler:

    Die header werden ja erst compilierd wenn main.cpp compiliert wird (in meinem beispiel) und daher würds nen fehler geben wennst z.b. andre header einbindest wo deine header eingebunden ist weil dann ein name für z.b. ein klasse doppelt exisitieren würd besser mich aber bitte aus falls das nicht stimmt! Ich hab bis jetzt meine header immer mit "Code Guard" betrieben (sag ich mal) auch mit templates also versteh ich ned ganz was daran nicht richtig sein soll!

    Was du sagst ist schon richtig. Was dein Denkfehler ist, ist oben schon beschrieben, ob du es nun wahrhaben willst oder nicht.
    Ich denke, ich mache dir mal ein kurzes Beispiel für dich:

    // foo.hpp
    // Klasse definieren, alles okay noch. ODR wird nicht verletzt!
    // Klasse wird nicht verändert und durch die Include Guards nur einmal in einer ÜE eingefügt
    
    #ifndef foo_hpp // include guards verwenden
    #define foo_hpp // mal schauen, was das bringt
    
    template <class T>
    class foo
    {
        public:
            void bar();
            void foobar();
    };
    
    // hmm, hier wirds kritisch werden, eine Definition im Header
    // es würde ein Template spezialisiert, d.h. sie ist nun in allen ÜE verfügbar,
    // in denen der Header eingebunden wird, ODR wird gebrochen!
    // die Lösung: entweder inline definieren oder in die cpp-Datei auslagern
    template<>
    void foo<int>::bar()
    {}
    
    // alles ok hier, darf gemacht werden ohne Bedenken
    // Compiler und Linker haben die Aufgabe, eine einzige Version des Templates mit dem jeweilligen Datentyp davon in die ausführbare Datei zu stecken
    // (s.u. und HumeSikkins Antwort weiter oben)
    template<class T>
    void foo<T>::foobar()
    {}
    
    #endif
    
    // foo.cpp
    #include "foo.hpp"
    
    // andere coole sachen hier machen
    
    // main.cpp
    #include "foo.hpp"
    
    int main()
    {
        foo<int> f; // Objekt generieren
        f.bar(); // aufrufen
        f.foobar(); // ebenfalls
    }
    

    So, nun das alles kompilieren. Nach deiner Meinung sollte das gehen, nicht?
    Die Include Guards regeln ja, dass alles bloß einmal eingebunden wird, ne?

    Aber, Schwups ->

    fluxer@fluxer test % g++ -o t foo.cpp main.cpp
    /tmp/cczgHezC.o(.text+0x0): In function `foo<int>::bar()':
    : multiple definition of `foo<int>::bar()'
    /tmp/ccYm38Os.o(.text+0x0): first defined here
    collect2: ld returned 1 exit status
    

    So, nun erkläre mir bitte das, warum das nicht geht. Deine schönen Include Guards sind drinen. 🙂

    (nur weil es ein intelligenter compiler vielleicht trotzdem checkt ists noch lang ned richtig! bestes beispiel sind viele java-scripts wo einfach am ende "}" zeichen weggelassen werden... funktioniert ist aber trotzdem falsch!!)

    Java kann ich nicht, kann dir nicht sagen, ob das stimmt. Zu deinem ersten Statement: Das sollte jeder Compiler können, weil es so, laut HumeSikkins, im Standard steht.

    das ganze ist ja ned so schwer:

    mann schreibts in nen header weil erst beim überstetzen einer cpp datei klar wird in welcher form die (z.b.) funktion gebraucht wird und dann wird sie erstellt! Glaub ned das sich daran was ändern wird und man jemals in ne cpp templates machen kann da der kompiler ja den (z.b.) type ned kennt! Das ist ne logische sache!

    Ja, da gebe ich dir ja auch Recht. Ich habe das nie bezweifelt. Lese es in meinem oberen Post nochmals nach. (export-Keyword, etc.) In cpp-Dateien können keine Templates stehen, sonst kriegste denn allbekannten Linkerfehler. (siehe anderes Posting) Aber wo bitte behaupte ich das? Oder magst du es, dich zu wiederholen?

    oder seh ich da was falsch?
    na ich hoffe jetzt ist klar was ich mein!
    mfg Manuelh87

    Ich denke schon, lese alles nochmal in Ruhe durch.



  • So, nun das alles kompilieren. Nach deiner Meinung sollte das gehen, nicht?
    Die Include Guards regeln ja, dass alles bloß einmal eingebunden wird, ne?

    hab ich das ned klar rüber gebracht... in JEDE Source Datei wos eingebunden wird nur einmal!!! ist doch logisch!!! Sry wenn das anders rübergekommen ist!

    ich mein das die spezialiesierte (also unabhängige) ganz normal in die source datei gehört ist doch klar! Kannst ja auch ned ne normale funktion in die header implementieren (außer inline!) das ist mir schon klar... mir gehts ja um die nicht spezialisierte!!

    Wollt dich auch ned darstellen das nix weißt wenn das so rüberkam sry! Aber ich denk halt dass das immer mit sovielen fachwörtern dargestellt wird und man es auch einfach machen kann!

    Was spricht jetzt deiner meinung nach gegen code -guards?? ich find die gehören aber bitte immer rein:

    //main.cc
    #include <cstdlib>
    #include <iostream>
    
    using namespace std;
    
    #include "foo.h" 
    #include "h2.h"
    
    int main() 
    { 
        foo<int> f;
        f.bar(); 
        f.foobar();    
        return 0;
    }
    
    //foo.h
    //#ifndef foo_hpp // code guard völlig unnötig?!
    //#define foo_hpp
    
    template <class T> 
    class foo 
    { 
        public: 
            void bar(); 
            void foobar(); 
    }; 
    
    template<class T> 
    void foo<T>::foobar() 
    {} 
    
    //#endif
    
    //h2.h
    #include "foo.h"
    
    //foo.cc
    #include "foo.h"
    
    template<> 
    void foo<int>::bar() 
    {}
    

    Ich hoffe jetzt ist klar warum man code guards benötigt!!!
    mfg Manuelh87



  • Die Include Guards helfen halt nicht, den Linkerfehler zu beseitigen. Natürlich benutzt man sie in den Headern, das ist ja klar. Gegen Include Guards haeb ich nie etwas gesagt, NUR sie helfen nicht in dieser Situation. Das hast du ja an meinem Beispiel gesehen. Ich denke, du und ich wissen, wann man Include Guards nun einsetzen muss. Das hast du ja mit deinem Beispiel rübergebracht. Noch irgendwelche Unklarheiten??



  • Ja hab jetzt auch gecheckt worauf du hinauswolltest! Ich dachte du möchtest sie weglassen! Aber sie haben natürlich bei dem beispiel keine auswirkungen! Ich dachte du meinst sie sind grundsätzlich wegzulassen und da musste ich dann protestieren!

    Das andre ist ja eh ziemlich logisch ich hätte das dann halt eher so beschrieben:

    // foo.h
    ...
    ...
    
    void hallo(void)
    {
    
    }
    
    // main.cc
    #include "foo.h"  // hallo() wird erstes mal in main.cc compiliert
    
    int main (void)
    {
      hallo();
      return 0;
    }
    
    // foo.cc
    #inlcude "foo.h"  // hallo() wird zweites mal compiliert
    

    hätte mir halt gedacht das es hier ohne template einfacher wäre zu erklärenen aber is im endeffekt eh wurscht! Wollt nur sagen das ich eben jetzt verstanden hab worauf du hinauswolltest!

    mfg Manuelh87



  • Das ist es ja grade, dass der Compiler bzw. der Linker bei Templates eine Ausnahme macht. Das hat ja HumeSikkins schon ewig gesagt. Gut, dass du nun auch das verstanden habe, was ich schon die ganze Zeit gesagt habe. 🙂


Anmelden zum Antworten