Open1

テンプレート引数に応じて文字リテラルの型を切り替える

Tetsuro MatsumuraTetsuro Matsumura

https://twitter.com/onihusube9/status/1411901300157997059

5種類の文字列リテラルの型を切り替えられるようにしてみました。

template<class T>
constexpr std::basic_string_view<T> get_hello_world_string() {
    return STRING_OF(T, "😎 Hello World");
}

STRING_OFはマクロで、第2引数の文字列リテラルをコピーしてプレフィックスを結合し、型が異なる5種類の文字列リテラルからなる実引数列を生成します。そして、5個の引数から指定した型に合うものを1つ選んで返す関数に渡すことで、指定した型の文字の配列の参照が得られます。

https://wandbox.org/permlink/N25RBbTWkLJ6TAuU

#include <iostream>
#include <string_view>

#define STRING_OF(t, x) select_type<t>((x), (L ## x), (u8 ## x), (u ## x), (U ## x))

template<class T, size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t;

template<size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t<char, N1, N2, N3, N4, N5> {
    using array_ref_t = const char (&)[N1];
    static constexpr array_ref_t select(const char (&s)[N1], const wchar_t (&)[N2], const char8_t (&)[N3], const char16_t (&)[N4], const char32_t (&)[N5]) noexcept {
        return s;
    }
};

template<size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t<wchar_t, N1, N2, N3, N4, N5> {
    using array_ref_t = const wchar_t (&)[N2];
    static constexpr array_ref_t select(const char (&)[N1], const wchar_t (&s)[N2], const char8_t (&)[N3], const char16_t (&)[N4], const char32_t (&)[N5]) noexcept {
        return s;
    }
};

template<size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t<char8_t, N1, N2, N3, N4, N5> {
    using array_ref_t = const char8_t (&)[N3];
    static constexpr array_ref_t select(const char (&)[N1], const wchar_t (&)[N2], const char8_t (&s)[N3], const char16_t (&)[N4], const char32_t (&)[N5]) noexcept {
        return s;
    }
};

template<size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t<char16_t, N1, N2, N3, N4, N5> {
    using array_ref_t = const char16_t (&)[N4];
    static constexpr array_ref_t select(const char (&)[N1], const wchar_t (&)[N2], const char8_t (&)[N3], const char16_t (&s)[N4], const char32_t (&)[N5]) noexcept {
        return s;
    }
};

template<size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
struct select_type_t<char32_t, N1, N2, N3, N4, N5> {
    using array_ref_t = const char32_t (&)[N5];
    static constexpr array_ref_t select(const char (&)[N1], const wchar_t (&)[N2], const char8_t (&)[N3], const char16_t (&)[N4], const char32_t (&s)[N5]) noexcept {
        return s;
    }
};

template<class T, size_t N1, size_t N2, size_t N3, size_t N4, size_t N5>
[[nodiscard]] constexpr auto select_type(const char (&s1)[N1], const wchar_t (&s2)[N2], const char8_t (&s3)[N3], const char16_t (&s4)[N4], const char32_t (&s5)[N5]) noexcept -> typename select_type_t<T, N1, N2, N3, N4, N5>::array_ref_t
{
    return select_type_t<T, N1, N2, N3, N4, N5>::select(s1, s2, s3, s4, s5);
}

template<class T>
constexpr std::basic_string_view<T> get_hello_world_string() {
    return STRING_OF(T, "😎 Hello World");
}

int main()
{
    std::cout << "sizeof" << std::endl;
    std::cout << sizeof(STRING_OF(char, "😎 Hello World")) << std::endl;
    std::cout << sizeof(STRING_OF(wchar_t, "😎 Hello World")) << std::endl;
    std::cout << sizeof(STRING_OF(char8_t, "😎 Hello World")) << std::endl;
    std::cout << sizeof(STRING_OF(char16_t, "😎 Hello World")) << std::endl;
    std::cout << sizeof(STRING_OF(char32_t, "😎 Hello World")) << std::endl;
    std::cout << "size()" << std::endl;
    std::cout << get_hello_world_string<char>().size() << std::endl;
    std::cout << get_hello_world_string<wchar_t>().size() << std::endl;
    std::cout << get_hello_world_string<char8_t>().size() << std::endl;
    std::cout << get_hello_world_string<char16_t>().size() << std::endl;
    std::cout << get_hello_world_string<char32_t>().size() << std::endl;
    return 0;
}

5種類もあるとコピペ感が強くなってきてよくないですね。可変引数テンプレートが使えないものか……。

似たような、というかほとんど同じテクニックがMS STLのテストコードで使われています。
https://github.com/microsoft/STL/blob/main/tests/std/tests/P0220R1_string_view/test.cpp#L265

MS STLとの違いは、文字列リテラルの長さを保存していることです。
C++20であれば、すべて consteval にしてもよさそうです。