templates und das export keyword



  • happystudent schrieb:

    Nach einiger Recherche scheint das export keyword genau dafür gedacht zu sein um das zu erreichen. Allerdings scheint es extrem schwierig für die Compilerbauer zu sein, dieses zu implementieren, angeblich schwieriger als drei beliebige andere Sprachfeatures zusammen genommen.

    Das export Keyword gibt es seit C++11 nicht mehr. Abgesehen davon gab es afaik nur einen einzigen Compiler, der es jemals unterstützt hat und die Wahrscheinlichkeit, dass du über diesen Compiler verfügst, schätze ich als sehr gering ein...



  • TNA schrieb:

    Keine wirklich Antwort auf deine Frage, aber bei uns wird diesem Problem meistens dadurch begegnet, dass wir alles was bei Templates nicht zum Interface gehört, in eine *.inl Datei auslagern, die wir am Ende der Header includieren. Ist aber wohl eher unüblich.

    Diese Technik kenn ich auch, hat aber auch einige Probleme... Bei mir gab es dann immer mal wieder Stress mit den Include-Guards wenn ich das so gemacht hab. Dann hat nur ein Clean und kompletter Rebuild geholfen, deswegen bin ich von dieser Methode wieder weg gegangen.

    bleeding_EDG schrieb:

    Ich weiss nicht, wie du recherchierst, aber offensichtlich nicht gut genug.

    ...

    Wenn es dir um Compile-Time-Boost geht und darum, die Implementation vor dem Kunden zu verstecken (was der eigentliche Sinn von export ist), dann lies http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1426.pdf

    Nö, das Paper kenn ich schon. Und da steht eindeutig "potential values of export: It enables a code organization style." Das ist (natürlich) nicht der einzige Sinn von export, aber um diesen spezifischen geht es mir hier eben.

    volkard schrieb:

    Mich hingegen stört es, daß man normale Klassen trennen muss, nur um Compilezeit zu sparen. Das Trennen halte ich für total unnatürlich.

    Ja, so kann man es natürlich auch sehen. Mir wäre es eigentlich egal wie rum man es jetzt macht, es sollte nur immer gleich und konsistent sein.

    bleeding_EDG schrieb:

    Es ist mit C++11 schon entfernt.

    Echt? Weil VS 2013 gibt die folgende Warnung: "warning C4237: 'export' keyword is not yet supported, but reserved for future use".



  • happystudent schrieb:

    Echt? Weil VS 2013 gibt die folgende Warnung: "warning C4237: 'export' keyword is not yet supported, but reserved for future use".

    Das ist auch gar nicht so verkehrt, da das 'export' keyword in C++11 reserviert aber unbenutzt ist.



  • happystudent schrieb:

    Diese Technik kenn ich auch, hat aber auch einige Probleme... Bei mir gab es dann immer mal wieder Stress mit den Include-Guards wenn ich das so gemacht hab. Dann hat nur ein Clean und kompletter Rebuild geholfen, deswegen bin ich von dieser Methode wieder weg gegangen.

    ???
    Dann liegt das Problem an den fehlerhaften Include-Guards oder zirkulären Abhängigkeiten. Es gibt kein prinzipielles Problem mit der inl-Technik.

    happystudent schrieb:

    bleeding_EDG schrieb:

    Es ist mit C++11 schon entfernt.

    Echt? Weil VS 2013 gibt die folgende Warnung: "warning C4237: 'export' keyword is not yet supported, but reserved for future use".

    Das Keyword ist noch reserviert, du darfst z.B. deine Variablen nicht "export" nennen. Es hat aber aktuell keine Bedeutung.



  • TNA schrieb:

    Das ist auch gar nicht so verkehrt, da das 'export' keyword in C++11 reserviert aber unbenutzt ist.

    bleeding_EDG schrieb:

    Das Keyword ist noch reserviert, du darfst z.B. deine Variablen nicht "export" nennen. Es hat aber aktuell keine Bedeutung.

    Ok alles klar, dann ist die Warning wohl unglücklich ausgedrückt.

    bleeding_EDG schrieb:

    ???
    Dann liegt das Problem an den fehlerhaften Include-Guards oder zirkulären Abhängigkeiten. Es gibt kein prinzipielles Problem mit der inl-Technik.

    Also ich weiß nicht mehr genau was das Problem war, aber es war auf jeden Fall nicht das einzige das ich damit hatte.

    Was mich auch sehr stört (und auch durch die .inl Methode nicht gelöst wird) ist zum Beispiel dass man keine unnamed-namespaces zum verstecken von Implementierungs-Details verwenden kann, wie es bei nicht-template Klassen üblich ist:

    // A.h
    class A {
        void foo();
    }
    
    // A.cpp
    namespace {
        void bar() {} // unsichtbar nach außen hin
    }
    
    void A::foo() {
        bar();
    }
    

    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.



  • 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