int template argument als string literal



  • hi,

    ich habe eine Klasse

    template <int D>
    class SomeClass
    {
    public:
      void doSomething(){
        callSomeFunctionThatTakesAString( "begin__" D "__end" );  // <--
      }
    
    }
    

    Ich hätte gern, dass der Parameterstring zur Compilezeit gebildet wird.

    gibt es da irgendeine Möglichkeit?

    Danke im Voraus.
    Vlad



  • Edit: Falsch gelesen. 😃
    Siehe unten.



  • vlad_tepesch schrieb:

    gibt es da irgendeine Möglichkeit?

    Bestimmt. Wofür brauchst du das? Geht es dir nur darum, einen einmaligen String für jede generierte Spezialisierung zu generieren?



  • ich will ja nicht den Typ sondern den Wert.

    Edit Edit:
    ok, es gab schon ein followup
    Text->zu eigenem Post



  • vlad_tepesch schrieb:

    ich will ja nicht den Typ sondern den Wert.

    Ja, eben 😃 Habe ich leider erst auf den zweiten Blick gesehen 😃



  • Warum?
    Ich will die Generierung zur Laufzeit sparen.
    Der Code soll auf einen relativ kleinen Mikrocontroller.



  • Vermutlich geht es, wenn du eine feste Anzahl an Zeichen für diese Zahl verwendest. Meine Idee:

    template <int D>
    class SomeClass
    {
        static const char string_instance[] = {'b', 'e', 'g', 'i', 'n', ' ',
        digit_char_template<D, 10>,
        digit_char_template<D, 9>,
        digit_char_template<D, 8>,
        digit_char_template<D, 7>,
        digit_char_template<D, 6>,
        digit_char_template<D, 5>,
        digit_char_template<D, 4>,
        digit_char_template<D, 3>,
        digit_char_template<D, 2>,
        digit_char_template<D, 1>,
        digit_char_template<D, 0>,
        ' ', 'e', 'n', 'd'};
    public:
      void doSomething(){
        callSomeFunctionThatTakesAString(string_instance);
      }
    };
    

    Wie du digit_char_template implementierst, kannst du dir ja selbst überlegen.



  • #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.


Log in to reply