Search code examples
c++templateswidechar

Use string litterals in a "char type" templated class


I have a template class in C++ which takes as a char_type template parameter the character type, such as char, wchar_t, char32_t, etc... The class then use std::basic_string<char_type> in the code.

Then somewhere in the class I fill a table of escaping sequences such as "&amp;". This does not work as depending on the template character type, we would need to use "&amp;", L"&amp;", U"&amp;"...

Is there a way to avoid specializing the template functions for initializing the table, for instance with some standard function for converting string litterals?

As these are escaping sequences, they do not contain anything else than ASCII characters.


Solution

  • I would do the following:

    template <typename char_type, size_t LENGTH>
    constexpr std::basic_string<char_type> literal(const char (&value)[LENGTH])
    {
        using string = std::basic_string<char_type>;
    
        string result{};
        result.reserve(LENGTH);
    
        std::copy(std::begin(value), std::end(value), std::back_inserter(result));
    
        return result; // rvo
    }
    

    You can use it this way:

    // Table of escaping sequences
    std::basic_string<char_type> escaping_sequences[] =
    {
        literal<char_type>("&amp"),
        literal<char_type>("&foo"),
        literal<char_type>("&bar"),
        ...
    }
    

    I've tested it in Ideone:

    literal<  char  >("test") // result: std::string
    literal<char32_t>("test") // result: std::basic_string<char32_t, std::char_traits<char32_t>, std::allocator<char32_t> >
    literal<char16_t>("test") // result: std::basic_string<char16_t, std::char_traits<char16_t>, std::allocator<char16_t> >
    

    Is untested for all the char types but hope it helps.

    Edit 1

    My bad, I just noticed that galinette almost answered the same as me before I did. The only difference between my code and the one from galinette is that I'm allocating the resulting string once with reserve instead of using the automatic allocation of push_back counting the number of characters at compile time, due to the use of LENGTH as a template parameter.

    Edit 2

    It is possible to avoid the final null character issue by substracting 1 to the end iterator:

    template <typename char_type, size_t LENGTH>
    constexpr std::basic_string<char_type> literal(const char (&value)[LENGTH])
    {
        using string = std::basic_string<char_type>;
    
        string result{};
        result.reserve(LENGTH - 1);
    
        std::copy(std::begin(value), std::end(value) - 1, std::back_inserter(result));
    
        return result; // rvo
    }
    

    Or, using std::copy_n instead of std::copy:

    template <typename char_type, size_t LENGTH>
    constexpr std::basic_string<char_type> literal(const char (&value)[LENGTH])
    {
        using string = std::basic_string<char_type>;
    
        string result{};
        result.reserve(LENGTH - 1);
    
        std::copy_n(std::begin(value), LENGTH - 1, std::back_inserter(result));
    
        return result; // rvo
    }