template
class StringTraits {
public:
static const CharType NULL_CHAR = \'\\0\';
static constexpr CharType* WHITESPACE_STR = \" \";
};
A variation of zett42 Method 2 above. Has the advantage of supporting all char types (for literals that can be represented as char[]) and preserving the proper string literal array type.
First the template functions:
template
constexpr
auto LiteralChar(
char A,
wchar_t W,
char8_t U8,
char16_t U16,
char32_t U32
) -> CHAR_T
{
if constexpr( std::is_same_v ) return A;
else if constexpr( std::is_same_v ) return W;
else if constexpr( std::is_same_v ) return U8;
else if constexpr( std::is_same_v ) return U16;
else if constexpr( std::is_same_v ) return U32;
}
template
constexpr
auto LiteralStr(
const char (&A) [SIZE],
const wchar_t (&W) [SIZE],
const char8_t (&U8) [SIZE],
const char16_t (&U16)[SIZE],
const char32_t (&U32)[SIZE]
) -> const CHAR_T(&)[SIZE]
{
if constexpr( std::is_same_v ) return A;
else if constexpr( std::is_same_v ) return W;
else if constexpr( std::is_same_v ) return U8;
else if constexpr( std::is_same_v ) return U16;
else if constexpr( std::is_same_v ) return U32;
}
Then the macros:
#define CMK_LC(CHAR_T, LITERAL) \
LiteralChar( LITERAL, L ## LITERAL, u8 ## LITERAL, u ## LITERAL, U ## LITERAL )
#define CMK_LS(CHAR_T, LITERAL) \
LiteralStr( LITERAL, L ## LITERAL, u8 ## LITERAL, u ## LITERAL, U ## LITERAL )
Then use:
template
class StringTraits {
public:
struct LC { // literal character
static constexpr CHAR_T Null = CMK_LC(CHAR_T, '\0');
static constexpr CHAR_T Space = CMK_LC(CHAR_T, ' ');
};
struct LS { // literal string
// can't seem to avoid having to specify the size
static constexpr CHAR_T Space [2] = CMK_LS(CHAR_T, " ");
static constexpr CHAR_T Ellipsis [4] = CMK_LS(CHAR_T, "...");
};
};
auto char_space { StringTraits::LC::Space };
auto wchar_space { StringTraits::LC::Space };
auto char_ellipsis { StringTraits::LS::Ellipsis }; // note: const char*
auto wchar_ellipsis { StringTraits::LS::Ellipsis }; // note: const wchar_t*
auto (& char_space_array) [4] { StringTraits::LS::Ellipsis };
auto (&wchar_space_array) [4] { StringTraits::LS::Ellipsis };
? syntax to get a local copy ?
Admittedly, the syntax to preserve the string literal array type is a bit of a burden, but not overly so.
Again, only works for literals that have the same # of code units in all char type representations.
If you want LiteralStr to support all literals for all types would likely need to pass pointers as param and return CHAR_T* instead of CHAR_T(&)[SIZE]. Don't think can get LiteralChar to support multibyte char.
[EDIT]
Applying Louis Semprini SIZE support to LiteralStr gives:
template ? SIZE_A :
std::is_same_v ? SIZE_W :
std::is_same_v ? SIZE_U8 :
std::is_same_v ? SIZE_U16 :
std::is_same_v ? SIZE_U32 : 0
>
constexpr
auto LiteralStr(
const char (&A) [SIZE_A],
const wchar_t (&W) [SIZE_W],
const char8_t (&U8) [SIZE_U8],
const char16_t (&U16) [SIZE_U16],
const char32_t (&U32) [SIZE_U32]
) -> const CHAR_T(&)[SIZE_R]
{
if constexpr( std::is_same_v ) return A;
else if constexpr( std::is_same_v ) return W;
else if constexpr( std::is_same_v ) return U8;
else if constexpr( std::is_same_v ) return U16;
else if constexpr( std::is_same_v ) return U32;
}
It is also possible to use a simpler syntax to create variables; for example, in StringTraits::LS can change to constexpr auto & so
static constexpr CHAR_T Ellipsis [4] = CMK_LS(CHAR_T, "...");
becomes
static constexpr auto & Ellipsis { CMK_LS(CHAR_T, "...") };
When using CMK_LS(char, "literal") any invalid char in literal are converted to '?' by VS 2019, not sure what other compilers do.