int template argument als string literal



  • #include <iostream>
    
    #define split_1(str1, pos)   (pos < sizeof str1 ? str1[pos] : '\0')
    #define split_2(str1, x)  split_1(str1 ,x), split_1(str1, x + 1)
    #define split_4(str1, x)  split_2(str1 ,x), split_2(str1, x + 2)
    #define split_8(str1, x)  split_4(str1 ,x), split_4(str1, x + 4)
    #define split_16(str1, x) split_8(str1 ,x), split_8(str1, x + 8)
    #define split_32(str1, x) split_16(str1, x), split_16(str1, x + 16)
    #define split(str1)    split_32(str1,  0)
    
    template<int i>
    struct getStr_impl
    {
            static char constexpr str[] = {"0123456789"[i % 10], split(getStr_impl<i / 10>::str)};
    };
    template<>
    struct getStr_impl<0>
    {
            static char constexpr str[] = {};
    };
    
    template<int i>
    struct getStr
    {
            static char constexpr str[] = {(i >= 0? '+' : '-'), split(getStr_impl<i>::str)};
    };
    
    template<int i>
    char constexpr getStr<i>::str[];
    
    int main()
    {
            std::cout << getStr<38>::str;
    }
    

    C++11, erstes Beispiel. Einziger Nachteil: Zahl wird rückwärts geschrieben 🤡

    P.S.: Das ist natürlich krasser Overbloat für so etwas Unnötiges. 😉


  • Mod

    template <typename T> struct identity {
        using type = T;
    };
    
    template <char... c> struct str
        : identity<str<c...>> {};
    
    template <typename... T> struct concat
        : concat<typename T::type...> {};
    
    template <char... c1, char... c2, typename... T> struct concat<str<c1...>, str<c2...>, T...>
        : concat<str<c1..., c2...>, T...> {};
    
    template <typename T> struct concat<T>
        : identity<T> {};
    
    template <int N, typename T = str<>> struct int2str
        : int2str<N/10, typename concat<str<'0'+N%10>, T>::type> {};
    
    template <char... c> struct int2str<0, str<c...>>
        : str<c...> {};
    template <> struct int2str<0, str<>>
        : str<'0'> {};
    
    template <int N, typename = typename concat<str<'b','e','g','i','n','_','_'>, int2str<N>, str<'_','_','e','n','d'>>::type>
    class foo;
    
    template <int N, char... c>
    struct foo<N, str<c...>>
    {   
        static constexpr char s[] = { c..., '\0' };
    };
    
    template <int N, char... c>
    constexpr char foo<N, str<c...>>::s[];
    
    #include <iostream>
    int main()
    {   
        std::cout << foo<0>::s << '\n';
        std::cout << foo<1>::s << '\n';
        std::cout << foo<42>::s << '\n';
    }
    


  • Leicht kürzer als das von camper, dafür weniger allgemein:

    template <int N, char... Digits> struct itos {
      typedef typename itos<N/10, (N%10) + '0', Digits...>::type type;
    };
    template <char... Digits> struct itos<0, Digits...> {
      typedef itos type;
      static constexpr char value[] = {
        'b','e','g','i','n','_','_',
        Digits...,
        '_','_','e','n','d', '\0' };
    };
    template <> struct itos<0> { typedef typename itos<0, '0'>::type type; };
    
    template <char... Digits> constexpr char itos<0, Digits...>::value[];
    
    int main()
    {
      std::cout << itos<0>::type::value << '\n';
      std::cout << itos<1>::type::value << '\n';
      std::cout << itos<42>::type::value << '\n';
    }
    


  • Wenn das auf nen uC soll, halte ich das Vorhaben für ziemlich wahnwitzig. Auf diese Art landet der Kram als String-Literal im RAM, und davon hast du zu wenig, um ihn auf so etwas zu verschwenden. Ich würde an deiner Stelle eher Boost.Preprocessor heranziehen (und darauf achten, dass die Ergebnisse im Progmem landen).



  • Hey Camper, wie geht das jetzt eigentlich mit dem Entfernen der ganzen Terminierungszeichen am Ende des Strings (diese Split-Version von dir)?


  • Mod

    seldon schrieb:

    Wenn das auf nen uC soll, halte ich das Vorhaben für ziemlich wahnwitzig. Auf diese Art landet der Kram als String-Literal im RAM, und davon hast du zu wenig, um ihn auf so etwas zu verschwenden. Ich würde an deiner Stelle eher Boost.Preprocessor heranziehen (und darauf achten, dass die Ergebnisse im Progmem landen).

    Von der Programmierung von µC habe ich keine Ahnung. wäre das Ergebnis anders mit so etwas wie

    #define X(z, n, text) D==n?"begin__" #n "__end":
        callSomeFunctionThatTakesAString( BOOST_PP_REPEAT(255, X, 0) "" );
    

    ?

    @Sone: hatten wir doch schon, oder?



  • Man müsste den Kram schon im Progmem generieren. Etwa

    #define MAKE_BEGIN_END(z, n, text) PSTR("begin__" #n "__end")
    #define BEGIN_END_COUNT soviel du halt brauchst
    
    char const *const begin_end_strs[] PROGMEM = {
      BOOST_PP_ENUM(BEGIN_END_COUNT, MAKE_BEGIN_END, _)
    };
    

    Und wie das mit Progmem-Strings halt so ist, muss man sie aus dem Progmem wieder rausholen (also pgm_read_byte(p) statt *p).

    Wenn es hier allerdings um mehr als um Ziffern geht, frisst auch dieser Ansatz irgendwann kilobyteweise Progmem. Man könnte das dadurch ein bisschen eindämmen, dass man die Zahlen ohne das drumherum als Strings im Progmem ablegt und halt vor Verarbeitung blockweise den ganzen String aus dem Progmem holt. Letztendlich ist aber die Frage, was der TE eigentlich genau damit vorhat, und wie knapp seine Rechenzeit wirklich ist. Meiner Erfahrung nach ist der Speicherplatz die strengere Begrenzung, wenn man nicht gerade irgendwas mit x Kilohertz genau befeuern muss und nicht genug PWM-Pins hatte.


  • Mod

    Re: trim, z.B. so

    #include <cstddef>
    #include <boost/preprocessor/repetition/repeat.hpp>
    
    template <typename T> struct identity {
        using type = T;
    };
    template <std::size_t... i> struct indexes
        : identity<indexes<i...>> {};
    
    template <std::size_t N, typename = indexes<>> struct make_indexes;
    template <std::size_t N, std::size_t... i> struct make_indexes<N, indexes<i...>>
        : make_indexes<N-1, indexes<0, i+1 ...>> {};
    template <std::size_t... i> struct make_indexes<0, indexes<i...>>
        : indexes<i...> {};
    
    template <char... c> struct str {
        static constexpr char data[] = { c..., '\0' };
    };
    template <char... c>
    constexpr char str<c...>::data[];
    
    template <size_t N, typename T, typename = typename make_indexes<N>::type> struct trim;
    template <size_t N, char... c, std::size_t... i> struct trim<N, str<c...>, indexes<i...>>
        : str<str<c...>::data[i]...> {};
    
    #define GETCHAR(z, pos, str)   (pos < sizeof str ? str[pos] : '\0'),
    
    #define STR(s) trim<sizeof(s)-1,str<BOOST_PP_REPEAT(256, GETCHAR, s) '\0'>>::data
    
    #include <iostream>
    int main()
    {
            std::cout << STR("foobar") << '\t' << sizeof STR("foobar") << '\n';
    }
    

    der ungekürzte String ist nicht ODR-used; sollte also auch nicht im kompilierten Code auftauchen.



  • camper schrieb:

    @Sone: hatten wir doch schon, oder?

    Nein, eigentlich nicht. Oder, falls mein Gedächtnis wieder Streiche spielt, kannst du mir den Link geben wo du es erklärst?



  • uiuiui, das versteht ja keiner, wenn man das einbaut, zumal ich nicht sicher bin, inwiefern der eigentliche Zielcompiler c++11 unterstützt.

    Trotzdem danke für die Vorschläge.
    Ich hatte gehofft es gibt etwas einfacheres.
    Aber so opfer ich lieber ein wenig Rechenzeit und Programmspeicher um den Code lesbar zu halten.



  • Ups, hab die 2. Seite übersehen

    @seldon:
    es ist kein AVR 😉
    Es ist ein Cortex M4. Der hat eine von Neuman Architektur und kann Code und Daten über einen Adressraum adressieren.

    Das mit dem Präprozessor Macros dürfte aber nicht Funktionieren, oder?
    Der Preprocessor läuft schließlich vor dem Compiler.
    Der Preprocessor baut mir also einen String zusammen, der das D enthält.



  • vlad_tepesch schrieb:

    Das mit dem Präprozessor Macros dürfte aber nicht Funktionieren, oder?
    Der Preprocessor läuft schließlich vor dem Compiler.
    Der Preprocessor baut mir also einen String zusammen, der das D enthält.

    Korrekt.


Anmelden zum Antworten