Länge einer Stringkonstante zur Übersetzungszeit ermitteln



  • Hallo,

    In "Mr. STLs" letztem Video http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Standard-Template-Library-STL-10-of-10
    fand ich wieder einmal wertvolles Grundwissen, das - warum auch immer - mir bisher nie untergekommen war: Das Auflösen eines nativen C-Array-Types per Template

    template<class T, size_t N> void foo(T const (&a)[N]) { ... }

    Die Methode finde ich äußerst praktisch für das Initialisieren von String- und Array-Klassen.
    Es hat mich immer schon etwas gestört, dass beim Erzeugen von std::string aus einem (char const 😉 intern nochmals eine strlen Implementierung aufgerufen wird, obwohl die Länge des Strings schon zur Übersetzungszeit bekannt ist.
    So manche vector-Instanz der Art std::vector(array, array + sizeof(array) / sizeof(array[0])) kann man so natürlich auch syntaktisch verkürzen.

    std::string myString1("Hello World"); // muss die Länge von "Hello World" nochmal messen
    
    template<size_t N> std::string build_string(char const (&str)[N])
    {
        return std::string(str, N - 1); // Länge ist bereits bekannt
    }
    std::string myString2(build_string("Hello World"));
    

    Dennoch vermute ich an der Sache einen Haken ... denn wenn es nicht irgend ein Problem damit gäbe, dann wäre diese Technik bereits in den Konstruktoren aller std::string oder std::vector Implementierungen drinnen, oder ?

    Ich arbeite ja häufig mit Stringkonstanten, macht es da also Sinn diverse Methoden zu überladen, um die Länge der Konstanten schon zur Übersetzungszeit zu ermitteln?

    Um Performance geht es mir da nicht zu sehr (ist ja kein großer Unterschied...), aber mir gefällt der Gedanke sehr, soviel Arbeit wie möglich vom Compiler erledigen zu lassen und nicht unnötig zur Laufzeit herumzurechnen.

    Ich möchte nur sicherstellen, dass mir diese "Optimierung" nicht irgend wann wegen diverse Seiteneffekte um die Ohren fliegt. Hat da jemand Erfahrung damit?

    Danke für eure Antworten 🙂

    lg, XOR



  • Wenn ich eine Funktion mit Parameter const char* aufrufe, heisst das noch lange nicht, dass es sich um eine Compilezeitkonstante handelt.

    Um Performance geht es mir da nicht zu sehr

    Aber das ist anscheinend der Grund hier fuer Templates, denn Typsicherheit kann es ja nicht sein. Auch gewinnst du dadurch nichts, da strlen in O(n) ablaeuft und die Konstruktion des Strings ebenfalls. Du verkomplizierst alles nur. If you have a shiny new hammer ... und so.

    gefällt der Gedanke

    Dir gefaellt es heute, morgen vielleicht nicht. Was ist mit anderen, die deinen Quelltext lesen, etc. Es gibt keinen objektiven Grund fuer diese "Optimierung".



  • Potentielles Problem:

    void foo()
    {
        char buf[512];
        sprintf(buf, "%d", 123);
    
        std::string const s1 = buf;
        std::string const s2 = build_string(buf);
    
        assert(s1 == s2);
    }
    

    Und wenn es um allgemeine Arrays geht, nicht nur char Arrays, dann gibt's noch ein potentielles Problem:

    template<class T, size_t N>
    void very_cool_array_size_trick(T const (&a)[N]) { /* ... */ }
    
    void bar()
    {
        struct something_with_internal_linkage { /* ... */ };
    
        something_with_internal_linkage arr[100];
    
        very_cool_array_size_trick(arr); // pustekuchen
    }
    


  • knivil schrieb:

    (...) If you have a shiny new hammer ... und so.

    Naja, immerhin fragt er hier um die "Dynamik" seines "shiny new hammer" besser zu verstehen, bevor er loshämmert.

    Was ist mit anderen, die deinen Quelltext lesen, etc. Es gibt keinen objektiven Grund fuer diese "Optimierung".

    Der Trick ist so alt und so einfach, dass man mMn. voraussetzen sollte, dass ein C++ Programmierer es verstehen kann (wenn es nötig wird es zu verstehen).
    (Man beachte: voraussetzen sollte != voraussetzen kann ;))



  • @hustbaer: Ja ... deshalb frag ich 😉 ... auf Puffer dieser Art hatte ich wirklich nicht gedacht, da ich diese nie einsetze.

    @knivil:
    Ich meinte ja auch nur eine zusätzliche Überladung im Sinne von:

    void addText(char const* txt, size_t len);
    void addText(char const* txt) { addText(txt, strlen(txt)); }
    template<size_t N> void addText(char const (&txt)[N]) { addText(txt, N - 1); }
    

    Ich sehe die Performance wirklich nicht kritisch, finde es aber dennoch sinnlos als Parameter eine Konstante zu übergeben, die eine Metainformation bereits enthält, die dann aber verworfen wird um danach einen Laufzeittest durchzuführen.
    Ich würde folgenden Code:

    void writeLog(const std::string& str) { fwrite(str.c_str(), 1, str.size(), f); }
    

    auch durch diesen ergänzen

    void writeLog(char const* str, size_t len) { fwrite(str, 1, len, f); }
    void writeLog(char const* str)             { writeLog(str, strlen(str)); }
    void writeLog(const std::string& str)      { writeLog(str.c_str(), str.size());}
    

    Natürlich wird das Interface dadurch komplizierter ... aber das Risiko gehe ich ein 😉

    Jedenfalls danke fürs Feedback 👍 - werde die std::string Implementierung vorerst nicht abändern 😃

    Aber es stimmt schon ... wenn man etwas Zeit findet sich Code durchzusehen, kommt einem schon der eine oder andere "shiny new hammer" in die Quere, und dann sieht man auch den Wald vor lauter Bäumen nicht.

    Happy new year!


Anmelden zum Antworten