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?
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.
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.
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.
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."'
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With