Commit string literals for different string classes

Problem

I am implementing a class in which I want to let the user select the type of string ( std::string , std::wstring , std::u16string , ...) using a template parameter. Currently, I cannot get string literals to match the selected string type: as soon as I solve for the 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>(); } 

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

Possible workaround

One way to overcome my problem is to implement several specializations of the hello_string() function. The problem is that this will result in multiple copies of each string literal - one for each string prefix. I think this is pretty ugly and there should be a better way.

Another way could be to select β€œnormal” string literals as default values ​​and the function will convert to different types of strings. While this avoids duplication of code, it will lead to unnecessary conversions of what is actually permanent.

+5
source share
1 answer

You can make yourself a macro. First, define a structure that wraps 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 in a beautiful macro:

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

Then your function will 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.

+2
source

All Articles