I'm trying to parse UUID expression using C++23, However compiling under clang 20.1.6 and gcc 15.1.0 find the operator as candidates yet it get ignored. here is my code:
union uuid_t {
unsigned char e[16];
};
template <char... Cs>
constexpr uuid_t operator ""_uuid() {
constexpr auto N = sizeof...(Cs);
static_assert(N == 36,
"UUID literal must be exactly 36 chars (8-4-4-4-12)");
// build a null-terminated buffer
constexpr char str[N + 1] = { Cs..., '\0' };
// dash checks
static_assert(str[8] == '-' &&
str[13] == '-' &&
str[18] == '-' &&
str[23] == '-',
"Dashes must be at 8,13,18,23");
// constexpr lambda to convert one hex char -> 0..15
constexpr auto hex_val = [](const char c) {
return static_cast<signed char>(
(c >= '0' && c <= '9') ? (c - '0'):
(c >= 'a' && c <= 'f') ? (10 + (c - 'a')):
(c >= 'A' && c <= 'F') ? (10 + (c - 'A')):
-1
);
};
uuid_t result{};
auto idx = 0;
for (auto& byte : result.e)
{
// skip dash if present
if constexpr(str[idx] == '-') ++idx;
constexpr auto hi = hex_val(str[idx++]);
if constexpr(str[idx] == '-') ++idx;
constexpr auto lo = hex_val(str[idx++]);
static_assert(hi != -1 && lo != -1,
"Only 0-9,A-F or 0-9,a-f is allowed in uuid expression.");
byte = static_cast<unsigned char>((hi << 4) | lo);
}
return result;
}
int main()
{
constexpr auto g = "00112233-4455-6677-8899-AABBCCDDEEFF"_uuid;
return 0;
}
using the operator uuid_t operator ""_uuid(const char* str, std::size_t)
works correctly, However I want to use the template syntax and I have no clue why it is failing.
This is my try https://godbolt.org/z/PnWT6zY7o
Thanks.
This form of literal operator:
template <char... Cs>
constexpr uuid_t operator ""_uuid()
is only used for numeric literals.
It simply isn't selected for string literals.
For string literals, C++20 instead provides this form (example taken from cppreference):
template<std::size_t N>
struct DoubleString
{
char p[N + N - 1]{};
constexpr DoubleString(char const(&pp)[N])
{
std::ranges::copy(pp, p);
std::ranges::copy(pp, p + N - 1);
}
};
template<DoubleString A>
constexpr auto operator""_x2()
{
return A.p;
}
Which means you can make your code work like this:
#include <cstddef>
union uuid_t {
unsigned char e[16];
};
template <std::size_t N>
struct UuidBuf {
static_assert(N == 37, // buffer contains \0
"UUID literal must be exactly 36 chars (8-4-4-4-12)");
uuid_t result{};
constexpr UuidBuf(char const (&str)[N]) {
// dash checks - can't use static_assert
if (!(str[8] == '-' && str[13] == '-' && str[18] == '-' &&
str[23] == '-'))
throw "Dashes must be at 8,13,18,23";
// constexpr lambda to convert one hex char -> 0..15
constexpr auto hex_val = [](const char c) {
return static_cast<signed char>(
(c >= '0' && c <= '9') ? (c - '0'):
(c >= 'a' && c <= 'f') ? (10 + (c - 'a')):
(c >= 'A' && c <= 'F') ? (10 + (c - 'A')):
throw "Only 0-9,A-F or 0-9,a-f is allowed in uuid expression."
);
};
auto idx = 0;
for (auto& byte : result.e) {
// skip dash if present
if (str[idx] == '-') ++idx;
auto hi = hex_val(str[idx++]);
if (str[idx] == '-') ++idx;
auto lo = hex_val(str[idx++]);
byte = static_cast<unsigned char>((hi << 4) | lo);
}
}
};
template <UuidBuf U>
constexpr uuid_t operator""_uuid() {
return U.result;
}
int main() {
constexpr auto g = "00112233-4455-6677-8899-AABBCCDDEEFF"_uuid;
return 0;
}