How to define string literal with character type that depends on template parameter?

前端 未结 5 1223
不知归路
不知归路 2021-02-06 07:47
template
class StringTraits {
public:
    static const CharType NULL_CHAR = \'\\0\';
    static constexpr CharType* WHITESPACE_STR = \" \";
};

         


        
5条回答
  •  一整个雨季
    2021-02-06 08:25

    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.

提交回复
热议问题