Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User-defined literal string: compile-time length check

I have a user-defined literal operator that only makes sense for strings of a specific length, like this:

constexpr uint16_t operator "" _int(const char* s, std::size_t len)
{
    return len == 2 ? s[0] | (s[1] << 8) : throw;
}

This works:

"AB"_int // equals 16961

But this also compiles, and I don't want it to:

"ABC"_int // throws at runtime

I tried static_assert(len == 2), but it isn't allowed in a constexpr function.

How can I make "ABC"_int cause an error at compile time?

like image 214
John Zwinck Avatar asked Nov 29 '17 01:11

John Zwinck


Video Answer


2 Answers

How can I make "ABC"_int cause an error at compile time?

By example: initialize a constexpr variable

constexpr auto foo = "ABC"_int;

Otherwise (if you don't force the compile time calculation in some way) the compiler doesn't compute (not mandatory but, in fact, is what happens) compile time but prepare the code for the run-time compilation.

like image 180
max66 Avatar answered Oct 05 '22 09:10

max66


Before C++20, you can wrap std::integral_constant with a macro to make throw trigger a compile error.

    constexpr uint16_t operator "" _int(const char* s, std::size_t len)
    {
        return len == 2 ? s[0] | (s[1] << 8) : throw;
    }

    void test()
    {
#define FORCE_CONSTANT(val) std::integral_constant<decltype(val), (val)>::value

        FORCE_CONSTANT("AB"_int);
        // FORCE_CONSTANT("ABC"_int); // error, expected compile-time constant expression
    }

And things become easy since C++20, user-defined literals are allowed to be string literal operator template. (cppref)

So, the following code will work as you expect.

template <size_t kCount>
struct template_str_buffer
{
    using char_type = char;

    consteval template_str_buffer(const char_type(&str)[kCount]) noexcept
    {
        for (size_t i = 0; i < kCount; ++i) {
            data[i] = str[i];
        }
    }

    char_type data[kCount];
    constexpr static size_t count = kCount - sizeof(char_type);
};

template <template_str_buffer kStrBuf>
consteval uint16_t operator""_int()
{
    static_assert(kStrBuf.count == 2);
    return kStrBuf.data[0] | (kStrBuf.data[1] << 8);
}

void test()
{
    "AB"_int;
    // "ABC"_int; // static assertion failed
}
like image 35
Sprite Avatar answered Oct 05 '22 09:10

Sprite