Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way for compile-time check of string user-defined literal?

I'm writing a user-defined string literal to convert names of months into their numbers. The expected usage of this literal is something like

"Nov"_m

which should return 11.

At the moment my code looks like

constexpr Duration operator ""_m(const char* str, size_t len)
{
    return convert_month_to_int(str, len);
}

where constexpr int convert_month_to_int(const char, size_t) is a function which does the actual conversion (or returns -1 if the month name is incorrect).

The problem is that I would like to show some kind of compile error if the string passed to this literal does not name any month. I tried using static_assert in the following way:

constexpr Duration operator ""_m(const char* str, size_t len)
{
    static_assert(convert_month_to_int(str, len) > 0, "Error");
    return convert_month_to_int(str, len);
}

but this does not work since the compiler is not sure that convert_month_to_int(str, len) will be a constant expression.

Is there any way of achieving this behavior?

like image 312
alexeykuzmin0 Avatar asked Aug 06 '16 09:08

alexeykuzmin0


People also ask

How does compiler process string literal?

The compiler scans the source code file, looks for, and stores all occurrences of string literals. It can use a mechanism such as a lookup table to do this. It then runs through the list and assigns the same address to all identical string literals.

What does literal mean in string literal?

A "string literal" is a sequence of characters from the source character set enclosed in double quotation marks (" "). String literals are used to represent a sequence of characters which, taken together, form a null-terminated string.

Is strlen compile time?

Hence, even if you had a strlen function called on a constant string in the tightest of the loop, it is still evaluated once at compile time.

What is string literal with example?

A string literal is a sequence of zero or more characters enclosed within single quotation marks. The following are examples of string literals: 'Hello, world!' '10-NOV-91' 'He said, "Take it or leave it."'


2 Answers

I've approached this problem in a different way, using neither enums nor string literals, and bad month names are detected even when not constructed as constexpr:

#include "date.h"

int
main()
{
    using namespace date::literals;
    auto m1 = nov;                           // ok
    static_assert(unsigned{nov} == 11, "");  // ok
    auto m2 = not_a_month;
    test.cpp:86:15: error: use of undeclared identifier 'not_a_month'
        auto m2 = not_a_month;
                  ^
    1 error generated.
}

The approach I used is to define a class type month which is documented to be a literal class type.

I then create constexpr instances of each month:

CONSTDATA date::month jan{1};
CONSTDATA date::month feb{2};
CONSTDATA date::month mar{3};
CONSTDATA date::month apr{4};
CONSTDATA date::month may{5};
CONSTDATA date::month jun{6};
CONSTDATA date::month jul{7};
CONSTDATA date::month aug{8};
CONSTDATA date::month sep{9};
CONSTDATA date::month oct{10};
CONSTDATA date::month nov{11};
CONSTDATA date::month dec{12};

(CONSTDATA is a macro to help compilers which aren't quite there with C++11 constexpr support limp along)

I also used the same technique for days of the week.

The above was all compiled using clang with -std=c++11. It will also work with gcc. The constexpr bits are broken in VS, but everything else works, including detecting bad month names at compile time.

like image 66
Howard Hinnant Avatar answered Nov 07 '22 11:11

Howard Hinnant


I agree with suggestion to use an enum instead.

But anyways, the usual way to signal an error like this in a constexpr function is to throw an exception.

constexpr Duration operator ""_m(const char* str, size_t len)
{
    return convert_month_to_int(str, len) > 0 ? convert_month_to_int(str, len) : throw "Error";
}

See also this question for instance.

like image 41
Chris Beck Avatar answered Nov 07 '22 12:11

Chris Beck