Templates + Stringliterale



  • Man kennt das, ein Template ist vom Zeichentyp abhängig. In der Definition mit Literalen zu arbeiten, macht sich dann nicht so schön. Ich habe ein wenig rumgebastelt:

    Eine Templatevariante:

    literal<char>("bla")::value
    literal<wchar_t>("bla")::value
    

    Lässt sich leicht handhaben, kostet aber für wchar_t zur Laufzeit.

    Dann hatte ich noch nen Makro, was aber immer eine Singlebyte und Multibyte Variante anlegt.

    Wie geht ihr damit um?



  • bunga bunga schrieb:

    Wie geht ihr damit um?

    Wofür genau brauchst du sowas?



  • dot schrieb:

    bunga bunga schrieb:

    Wie geht ihr damit um?

    Wofür genau brauchst du sowas?

    Eigentlich nur aus Bequemlichkeit, weil ich nicht spezialisieren möchte. Die Implementation ist identisch, außer das die Literale angepasst werden müssen.



  • Ich kann mir trotzdem einfach nicht vorstellen, wofür man sowas brauchen sollte. Kannst du mir ein Anwendungsbeispiel geben?



  • pumuckl schrieb:

    Nathan schrieb:

    Es geht sogar viel einfach wie ich gerade feststellen konnte.

    string literal = "Das Literal, immer vom Typ string.";
    funktionDieLiteralBraucht(basic_string<E, Traits>(literal.begin(), literal.end()));
    

    Klappt bei mir (gcc und C++11) wunderbar.

    Gute Idee, danke 🙂

    template <class E, class T = std::char_traits<E>, unsigned N = 0>
    std::basic_string<E, T> convertLiteral(char const(& lit)[N])
    { return std::basic_string<E, T>(lit, lit+N); }
    
    template< typename E, typename Traits >
    std::basic_string< E, Traits > replaceElements( std::basic_string< E, Traits > str ) {
        auto tag = convertLiteral<E, Traits>("Element");
        auto pos = str.find(tag);
        if (pos != decltype(str)::npos)
            str.replace(pos, tag.length(), convertLiteral<E, Traits>("Tnemele"));
        return str;
    }
     
    template <typename E>
    auto replaceElements(E const* ptr) -> std::basic_string<E>
    { return replaceElements(std::basic_string<E>(ptr)); }
    

    Trotzdem kein wchar_t bitte 😛



  • Wäre doch eigentlich überall ganz praktisch, wo es um Ein/Ausgabe geht.

    Beispielsweise wenn der Ausgabeoperator überladen werden soll:

    template <typename charT>
    std::basic_ostream<charT>& operator <<(std::basic_ostream<charT> s, const Foo& f)
    {
        s << literal_cast<charT>("bar() = ") << f.bar() << literal_cast<charT>("baz() = ") << f.baz();
    }
    

    Bei der Instanziierung werden dann einfach die richtigen Adressen eingesetz und fertig.



  • Wenn du dich damit abfinden kannst

    'b','a','r','(',')',' ','=',' '
    

    zu schreiben gibt es einige schöne Lösungen. Ansonsten eher nicht.

    Das String-Handling zur Compile-Zeit finde ich echt schwach gelöst von C++11.

    @Nathan: Inwiefern kostet das nicht zur Laufzeit?



  • Danek schonmal, an string hatte ich gar nicht gedacht.

    Wie kann das funktionieren?

    auto tag = convertLiteral<E, Traits>("Element");
    

    tag müsste doch eigenlich leer sein?!



  • Das ist C++11, google mal auto.

    Und die Laufzeitkosten sind dank inline und move quasi null.



  • @Nathan: Mit move hat das rein gar nichts zu tun, der Punkt ist halt, dass bei dir das String-Literal als char[] im Datensegment liegt und dann zur Laufzeit darüberiteriert werden muss um jedes Zeichen in wchar_t zu konvertieren.

    Ich habe hier eine Version, bei der ein wchar_t[] im Datensegment abgelegt wird:

    // <kopiert author="camper">
    template <typename T> struct identity_ { using type = T; };
    template <int... i> struct index_list : identity_<index_list<i...>> {};
    template <int N, typename = index_list<>> struct make_index_list_;
    template <int N, typename T = index_list<>>
    using make_index_list = typename make_index_list_<N, T>::type;
    template <int N, int... i>
    struct make_index_list_<N, index_list<i...>>
      : make_index_list<N-1, index_list<0, 1+i...>> {};
    template <int... i>
    struct make_index_list_<0, index_list<i...>>
      : index_list<i...> {};
    // </kopiert>
    
    template <typename C, unsigned N>
    struct string_lit { C data[N]; };
    
    template <class C, unsigned N, int... I>
    constexpr string_lit<C, N> convert_literal_(char const(& lit)[N],
                                                index_list<I...>)
    {
      return {{ static_cast<C>(lit[I])... }};
    }
    
    template <class C, unsigned N = 0>
    constexpr string_lit<C, N> convert_literal(char const(& lit)[N])
    {
      return convert_literal_<C, N>(lit, make_index_list<N>{});
    }
    
    void gtmarg()
    {
      constexpr auto lit = convert_literal<wchar_t>("hello world, this is a very very very long string and it has to be stored in a very very constant array");
      printme(lit.data);
    }
    


  • Nathan schrieb:

    Das ist C++11, google mal auto.

    Auto hat damit gar nichts zu tun, ich war überrascht, dass man die Größe des Arrays einfach über das Template-Pattern abgreifen kann und sie nicht explizit angeben muss.

    @gtmarg + camper
    Danke, genau das was ich suche. Leider noch zuviel C++11 für den VC11. Ich habs vorübergehend erstmal so gelöst:

    template <typename CharT>
    struct string_literal;
    
    template <>
    struct string_literal<char>
    {
    	string_literal(const char* lit) : data(lit) {}
    	const char* data;
    	inline const char* c_str() {return data;}
    };
    
    template <>
    struct string_literal<wchar_t>
    {
    	template <std::size_t SizeV>
    	string_literal(char const(& lit)[SizeV]) : data(lit, lit + SizeV - 1) {}
    	std::wstring data;
    	inline const wchar_t* c_str() {return data.c_str();}
    };
    
    template <typename CharT>
    inline std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& s, const string_literal<CharT>& l)
    {
    	return s << l.data;
    }
    

    Btw, macht es sind mit

    #if __cplusplus >= 201103L
    

    den Standard zu prüfen?

    MfG


Anmelden zum Antworten