templates und das export keyword



  • Es gibt keine Templateklassen und auch keine Templatefunktionen. Es gibt nur Klassentemplates und Funktionstemplates. Ein Template an sich ist keine Klasse bzw. Funktion. Ein Template ist ein Template (eine Vorlage). Ein Klassentemplate ist eine Vorlage nach der eine Klasse generiert werden kann. Ein Funktionstemplate ist eine Vorlage nach der eine Funktion generiert werden kann. Damit der Compiler eine Klasse bzw. Funktion nach einer Vorlage generiert kann, muss er diese Vorlage kennen. Die Definition eines Template "zu verstecken" widerspricht dem Konzept von Templates...

    Unnamed namespaces dienen dazu, Dingen internal Linkage zu verpassen. Du kannst selbstverständlich templates in einen unnamed namespace packen, dann werden alle Instanzen dieser Templates internal Linkage haben. Die Frage ist, wie sinnvoll das ist...



  • happystudent schrieb:

    und das geht halt auch nicht wenn A eine template Klasse ist. Stattdessen muss man dem Namespace einen Namen geben und sich an Konventionen halten.

    Ich habe noch nie jemanden gesehen, der einen detail-Namespace angefasst hat.

    C++ hat Unschönheiten, damit muss man als Programmierer leben. Ein export-ähnliches Feature fehlt. export funktioniert aber nicht aus verschiedenen Gründen. Das Hauptproblem ist, dass die Instanzierung von den aktuellen Symbolen abhängt. Beispiel:

    template <typename T> struct S {
      S() { T x; f(x); }
    };
    
    struct MyStruct {};
    
    // a.cpp                                                                                                                                               
    namespace {
      void f(MyStruct) {}
    }
    void a() { S<MyStruct> s; } // Konstruktor ruft f(MyFunc) aus a.cpp auf
    
    // b.cpp                                                                                                                                               
    namespace {
      void f(MyStruct) {}
    }
    void b() { S<MyStruct> s; } // Konstruktor ruft f(MyFunc) aus b.cpp auf
    

    export kann also nicht das machen, was du vorschlägst. Worauf es raus läúft, ist, dass der Code erst vom Linker kompiliert werden kann. export ist broken.

    Bessere Lösungen sind momentan nicht absehbar. Module wären eine gute Lösung kommen aber frühestens mit C++17 und selbst das ist sehr unwahrscheinlich.



  • dot schrieb:

    Unnamed namespaces dienen dazu, Dingen internal Linkage zu verpassen. Du kannst selbstverständlich templates in einen unnamed namespace packen, dann werden alle Instanzen dieser Templates internal Linkage haben. Die Frage ist, wie sinnvoll das ist...

    Darum gings ja auch gar nicht, sondern darum dass man keinen details-namespace braucht. Wie in meinem Beispiel muss das im unnamed namespace noch nichtmal ein Funktions-template sein, sondern halt irgendwas das nach außen hin unsichtbar/gekapselt sein soll.

    bleeding_EDG schrieb:

    export kann also nicht das machen, was du vorschlägst. Worauf es raus läúft, ist, dass der Code erst vom Linker kompiliert werden kann. export ist broken.

    Aha, Ok das leuchtet ein. Dann klappt das natürlich nicht, danke für die Erklärung.

    bleeding_EDG schrieb:

    Bessere Lösungen sind momentan nicht absehbar. Module wären eine gute Lösung kommen aber frühestens mit C++17 und selbst das ist sehr unwahrscheinlich.

    Ok... also Module würden das ganze Problem lösen?

    Würden Module dann auch das Problem lösen dass man immer den gesamten Source-Code braucht wenn man templates verwendet oder könnte man damit dann eine template Bibliothek (oder: Bibliotheks-template) kompilieren und nur den Header ausliefern und trotzdem noch templates verwenden?

    Oder bleibt das weiterhin wunschdenken, auch mit Modules?



  • happystudent schrieb:

    dot schrieb:

    Unnamed namespaces dienen dazu, Dingen internal Linkage zu verpassen. Du kannst selbstverständlich templates in einen unnamed namespace packen, dann werden alle Instanzen dieser Templates internal Linkage haben. Die Frage ist, wie sinnvoll das ist...

    Darum gings ja auch gar nicht, sondern darum dass man keinen details-namespace braucht. Wie in meinem Beispiel muss das im unnamed namespace noch nichtmal ein Funktions-template sein, sondern halt irgendwas das nach außen hin unsichtbar/gekapselt sein soll.

    Und wie ich in meinem Posting zu illustrieren versucht habe, macht dies im Kontext mit Templates keinen Sinn. Ein Template ist keine Klasse oder Funktion, sondern eine Vorlage, nach der eine Klasse bzw. Funktion generiert werden kann. Eine solche Vorlage an sich kann nicht kompiliert werden, das macht rein konzeptionell keinen Sinn. Kompiliert wird eine konkrete Instanz dieser Vorlage. Und um eine solche Instanz generieren zu können, muss die Definition der Vorlage bekannt sein. Du kannst konkrete Instanzen eines Template explizit erzeugen und diese dann separat kompilieren (Stichwort extern template ). Aber ein Template an sich kann nicht separat kompiliert werden, das macht keinen Sinn...

    happystudent schrieb:

    Würden Module dann auch das Problem lösen dass man immer den gesamten Source-Code braucht wenn man templates verwendet oder könnte man damit dann eine template Bibliothek (oder: Bibliotheks-template) kompilieren und nur den Header ausliefern und trotzdem noch templates verwenden?

    Oder bleibt das weiterhin wunschdenken, auch mit Modules?

    Das Problem ist prinzipieller Natur. Abgesehen davon, dass im Moment niemand so recht weiß, was so ein "Modul" eigentlich genau sein soll, kannst du selbst unter der Annahme, dass wir "Module" hätten, Templates bestenfalls separat parsen...



  • dot schrieb:

    happystudent schrieb:

    Darum gings ja auch gar nicht, sondern darum dass man keinen details-namespace braucht. Wie in meinem Beispiel muss das im unnamed namespace noch nichtmal ein Funktions-template sein, sondern halt irgendwas das nach außen hin unsichtbar/gekapselt sein soll.

    Und wie ich in meinem Posting zu illustrieren versucht habe, macht dies im Kontext mit Template rein konzeptionell keinen Sinn. Ein Template ist keine Klasse oder Funktion, sondern eine Vorlage, nach der eine Klasse bzw. Funktion generiert werden kann. Eine solche Vorlage an sich kann nicht kompiliert werden, das macht rein konzeptionell keinen Sinn. Kompiliert wird eine konkrete Instanz dieser Vorlage. Und um eine solche Instanz generieren zu können, muss die Definition der Vorlage bekannt sein. Du kannst konkrete Instanzen eines Template explizit erzeugen und diese dann getrennt kompilieren. Aber ein Template an sich kann nicht separat kompiliert werden, das macht keinen Sinn.

    Warum soll es keinen Sinn machen Implementierungsdetails bei einem Klassen-Template zu kapseln? Ich weiß dass ein Template eine Vorlage ist nach der eine Klasse/Funktion generiert wird. Trotzdem will man ja manchmal irgendwelche Unterfunktionen die für den Benutzer des instanziierten Klassen-Templates nicht sichtbar sein sollen, verstecken.

    Ein template ist eine Vorlage. Ich will dieser Vorlage jetzt sagen dass sie für jede Instanziierung eine nach außen hin unsichtbare Funktion miterzeugt. Wo ist da der Konzeptionelle Fehler? Der einzige Fehler ist doch dann dass C++ kein Sprachmittel bereit stellt um das sauber zu lösen und dass das verstecken mittels unnamed namespaces nur ein Hack ist?



  • happystudent schrieb:

    Ich weiß dass ein Template eine Vorlage ist nach der eine Klasse/Funktion generiert wird. Trotzdem will man ja manchmal irgendwelche Unterfunktionen die für den Benutzer des instanziierten Klassen-Templates nicht sichtbar sein sollen, verstecken.

    Na dann packt man diese Unterfunktionen eben in eine separate .cpp Datei?

    happystudent schrieb:

    Ein template ist eine Vorlage. Ich will dieser Vorlage jetzt sagen dass sie für jede Instanziierung eine nach außen hin unsichtbare Funktion miterzeugt.

    Pack dein Template in einen unnamed namespace und du bekommst genau das, wonach du hier fragst. Das ist aber nicht, was du willst. Was du eigentlich willst, ist, dass die Definition des Template versteckt ist. Und das macht, wie bereits beschrieben, keinen Sinn.

    happystudent schrieb:

    Wo ist da der Konzeptionelle Fehler? Der einzige Fehler ist doch dann dass C++ kein Sprachmittel bereit stellt um das sauber zu lösen und dass das verstecken mittels unnamed namespaces nur ein Hack ist?

    Nein; das Problem ist prinzipieller Natur, wie im Posting oben gerade noch hinzueditiert. Unabhängig von irgendwelchen Sprachmitteln, kannst du ein Template rein prinzipiel bestenfalls separat parsen. Angenommen, wir hätten einen Compiler und entsprechende Sprachmittel, die es ermöglichen, die compilerinternen Datenstrukturen entsprechend in Dateien, nennen wir diese mal "Module", zu serialisieren und von dort wieder einzulesen. Wie static Libraries, sind diese "Module" dann gerade mal für eine konkrete Version eines konkreten Compilers brauchbar. Und selbst dann kann gerade mal das Parsen, nicht aber die Instanzierung der Templates separat gehandhabt werden. Ok; du brauchst dazu nun nichtmehr direkt den Source Code, aber etwas vom Informationsgehalt her Äquivalentes...


  • Mod

    bleeding_EDG schrieb:

    Das Hauptproblem ist, dass die Instanzierung von den aktuellen Symbolen abhängt. Beispiel:

    template <typename T> struct S {
      S() { T x; f(x); }
    };
    
    struct MyStruct {};
    
    // a.cpp                                                                                                                                               
    namespace {
      void f(MyStruct) {}
    }
    void a() { S<MyStruct> s; } // Konstruktor ruft f(MyFunc) aus a.cpp auf
    
    // b.cpp                                                                                                                                               
    namespace {
      void f(MyStruct) {}
    }
    void b() { S<MyStruct> s; } // Konstruktor ruft f(MyFunc) aus b.cpp auf
    

    export kann also nicht das machen, was du vorschlägst. Worauf es raus läúft, ist, dass der Code erst vom Linker kompiliert werden kann. export ist broken.

    Wobei in diesem Falle allerdings die ODR verletzt wird. Schlimmer: diese Verletzung führt nur zu UB.


  • Mod

    Wobei in diesem Falle allerdings die ODR verletzt wird.

    Warum "allerdings"? Gerade deswegen hat er doch das Beispiel gebracht.

    Schlimmer: diese Verletzung führt nur zu UB.

    Kann der Linker so etwas prinzipiell erkennen und Warnungen geben (auch wenn er false positives meldet)?



  • Arcoth schrieb:

    Schlimmer: diese Verletzung führt nur zu UB.

    Kann der Linker so etwas prinzipiell erkennen und Warnungen geben (auch wenn er false positives meldet)?

    Nun, zumindest in diesem Fall sollte der Linker zwei verschiedene Definitionen der selben Funktion ( S<MyStruct>::S() ) sehen...



  • dot schrieb:

    Es gibt keine Templateklassen und auch keine Templatefunktionen.

    Manchmal werden mit Templateklassen und auch Templatefunktionen Klassen und Funktionen bezeichnet die aus Templates generiert wurden. Das halte ich aber auch nicht für sehr hilfreich.



  • dot schrieb:

    as du eigentlich willst, ist, dass die Definition des Template versteckt ist. Und das macht, wie bereits beschrieben, keinen Sinn.

    Die Frage ist vor wem man diese verstecken möchte. Der Compiler braucht sie in den meisten Fällen. Der Programmierer, den nur die API interessieren soll, nicht.


  • Mod

    Arcoth schrieb:

    Wobei in diesem Falle allerdings die ODR verletzt wird.

    Warum "allerdings"? Gerade deswegen hat er doch das Beispiel gebracht.

    Dann habe ich das falsch verstanden. Es machte den Eindruck, als ob der Code so in Ordnung wäre, aber bei Verwendung von export nicht mehr funktionieren könnte.

    Arcoth schrieb:

    Schlimmer: diese Verletzung führt nur zu UB.

    Kann der Linker so etwas prinzipiell erkennen und Warnungen geben (auch wenn er false positives meldet)?

    Was tun, wenn sich die Verletzung auf Symbole bezieht, die der Linker gar nicht kennt (also z.B. das Beispiel abändern und statt unbenannter Nammensräume statische Funktionen nehmen)?


Anmelden zum Antworten