Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Yet another string literal, UDL labyrinth

NOTE: This is MSVC, C++17 question.

Disclaimer: I know this has been attempted, and yes I was trying to find a relevant SO answer.

I can code the UDL, to achieve transforming numeric literals to std::array, at compile time:

    // std::array{ '1','2','3' }
    constexpr auto a_1 = 123_std_char_array;

    // std::array{ '0','x','1','2' }
    constexpr auto a_2 = 0x12_std_char_array;

    // std::array{ '4'.'2','.','1','3' }
    constexpr auto a_3 = 42.13_std_char_array;

And this is the UDL, I made:

    template< char ... Chs >
inline constexpr decltype(auto) operator"" _std_char_array( )
{
    // append '\0'
    return  std::array { Chs..., char(0) } ;
}

Amazing, snazzy, modern, blah,blah,blah ... But.

The Question

How do I code an UDL to allow for this:

    // std::array {'S','t','r','i','n','g'}
    constexpr auto std_char_array_buff_ = 
         "String"_std_char_array ;

In MSVC, C++17, please.

The Confession

I know UDL to "catch" the string literal has to have this footprint:

  inline auto operator"" _X( const char*, size_t);

I know how to transform string literal to std::array, at compile time. But without UDL. Please see here, for inspiration.

Yes, I know C++20 will have UDL template addition, and GCC, clang have something else right now. Although I do not see how is any of that helping me.

And lastly, I know I can do this:

     constexpr auto string_view_ = "String"sv ;
like image 277
Chef Gladiator Avatar asked Aug 19 '19 07:08

Chef Gladiator


1 Answers

Unfortunately, this does not seem to be possible in C++17. A user-defined-string-literal can only match operator""X(str, len) per [lex.ext]/5. Then, the len is a function argument, and function arguments cannot be converted to template arguments. Just like you can't do this:

template <int N>
struct S {};

constexpr auto f(int n)
{
    return S<n>{}; // no, n is not guaranteed to be known at compile time
}

"foo"sv works because the size isn't a template parameter of std::basic_string_view, but a "runtime" property instead which happens to benefit from constexpr. You can't do it with std::array because the size is a template parameter of std::array.

make_array works because it is not a literal operator, so it can take the size as a template parameter instead of a function parameter. Then, it can pass the template parameter to std::array. A literal operator can't do that.


In C++20, I think we can use a wrapper type like this:

template <std::size_t N>
struct helper {
    std::array<char, N> string;

    template <std::size_t... Is>
    constexpr helper(const char (&str)[N + 1], std::index_sequence<Is...>)
        :string{str[Is]...}
    {
    }
    constexpr helper(const char (&str)[N + 1])
        :helper{str, std::make_index_sequence<N>{}}
    {
    }
};

template <std::size_t N>
helper(const char (&str)[N]) -> helper<N - 1>;

and then use a string literal operator template:

template <helper str> // placeholder type for deduction
constexpr auto operator""_S()
{
    return str.string;
}

static_assert("foo"_S == std::array{'f', 'o', 'o'});

C++20 is not finalized yet though, so I cannot speak for sure.

like image 111
L. F. Avatar answered Nov 12 '22 01:11

L. F.