c++c++23user-defined-literals

gcc/clang unresolved user-defined literal template operator""


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.


Solution

  • 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;
    }
    

    https://godbolt.org/z/q7WPrPvTj