Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fitting string literals for different string classes

The problem

I am implementing a class where I want to let the user choose the string type (std::string, std::wstring, std::u16string, ...) via a template parameter. I currently fail to make the string literals fit the chosen string type: Once I decide for a literal prefix ("hello" vs. L"hello" vs. u"hello" vs. U"hello"), I get compilation errors for all incompatible string classes.

Toy example

As an example, consider the following code (compile with --std=c++11):

#include <string>

template<typename StringType>
void hello_string()
{
    StringType result("hello");
}

int main()
{
    // works
    hello_string<std::string>();
    hello_string<std::basic_string<char>>();

    // the code below does not compile
    hello_string<std::wstring>();
    hello_string<std::basic_string<unsigned char>>();
    hello_string<std::u16string>();
}

Function hello_string() shows the essence of what I want to do: have a string type as template parameter, and assign string literals to variables of this type.

Possible workaround

One way to overcome my problem would be to implement several specializations of the hello_string() function. The problem is that this would lead to several copies of each string literal - one for each string literal prefix. I think this is rather ugly, and there must be better way.

Another way could be to chose "normal" string literals as default values and have functions do a conversion to the different string types. While this would avoid code duplication, it would introduce unnecessary conversions of something that is actually constant.

like image 600
Niels Lohmann Avatar asked Oct 31 '22 05:10

Niels Lohmann


1 Answers

You can make yourself a macro. First define a struct that wraps the char-choosing:

namespace details {

    template<typename T>
    struct templ_text;

    template<>
    struct templ_text <char>
    {
        typedef char char_type;
        static const char_type * choose(const char * narrow, const wchar_t * wide, const char16_t* u16, const char32_t* u32) { return narrow; }
        static char_type choose(char narrow, wchar_t wide, char16_t u16, char32_t u32) { return narrow; }
    };

    template<>
    struct templ_text < wchar_t >
    {
        typedef wchar_t char_type;
        static const char_type* choose(const char * narrow, const wchar_t * wide, const char16_t* u16, const char32_t* u32) { return wide; }
        static char_type choose(char narrow, wchar_t wide, char16_t u16, char32_t u32) { return wide; }
    };

    template<>
    struct templ_text < char16_t >
    {
        typedef char16_t char_type;
        static const char_type* choose(const char * narrow, const wchar_t * wide, const char16_t* u16, const char32_t* u32) { return u16; }
        static char_type choose(char narrow, wchar_t wide, char16_t u16, char32_t u32) { return u16; }
    };

    template<>
    struct templ_text < char32_t >
    {
        typedef char32_t char_type;
        static const char_type* choose(const char * narrow, const wchar_t * wide, const char16_t* u16, const char32_t* u32) { return u32; }
        static char_type choose(char narrow, wchar_t wide, char16_t u16, char32_t u32) { return u32; }
    };
}

Wrap it into a nice macro:

#define TEMPL_TEXT(Ch, txt) details::templ_text<Ch>::choose(txt, L##txt, u##txt, U##txt)

Then your function would be:

template<typename StringType>
void hello_string()
{
    StringType result(TEMPL_TEXT(typename StringType::value_type, "Hello"));
}

I think that unused copies of the string will be optimized away.

like image 181
Databyte Avatar answered Nov 27 '22 22:11

Databyte