Skip to content

Instantly share code, notes, and snippets.

@AlexBAV
Last active Jun 11, 2021
Embed
What would you like to do?
Constexpr GUID parsing (parsing string GUIDs at compile-time)
//-------------------------------------------------------------------------------------------------------
// constexpr GUID parsing
// Written by Alexander Bessonov
//
// Licensed under the MIT license.
//-------------------------------------------------------------------------------------------------------
#pragma once
#include <stdexcept>
#include <string>
#include <cassert>
#include <cstdint>
#if !defined(GUID_DEFINED)
#define GUID_DEFINED
struct GUID {
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
};
#endif
namespace guid_parse
{
namespace details
{
constexpr const size_t short_guid_form_length = 36; // XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
constexpr const size_t long_guid_form_length = 38; // {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
//
constexpr int parse_hex_digit(const char c)
{
using namespace std::string_literals;
if ('0' <= c && c <= '9')
return c - '0';
else if ('a' <= c && c <= 'f')
return 10 + c - 'a';
else if ('A' <= c && c <= 'F')
return 10 + c - 'A';
else
throw std::domain_error{ "invalid character in GUID"s };
}
template<class T>
constexpr T parse_hex(const char *ptr)
{
constexpr size_t digits = sizeof(T) * 2;
T result{};
for (size_t i = 0; i < digits; ++i)
result |= parse_hex_digit(ptr[i]) << (4 * (digits - i - 1));
return result;
}
constexpr GUID make_guid_helper(const char *begin)
{
GUID result{};
result.Data1 = parse_hex<uint32_t>(begin);
begin += 8 + 1;
result.Data2 = parse_hex<uint16_t>(begin);
begin += 4 + 1;
result.Data3 = parse_hex<uint16_t>(begin);
begin += 4 + 1;
result.Data4[0] = parse_hex<uint8_t>(begin);
begin += 2;
result.Data4[1] = parse_hex<uint8_t>(begin);
begin += 2 + 1;
for (size_t i = 0; i < 6; ++i)
result.Data4[i + 2] = parse_hex<uint8_t>(begin + i * 2);
return result;
}
template<size_t N>
constexpr GUID make_guid(const char(&str)[N])
{
using namespace std::string_literals;
static_assert(N == (long_guid_form_length + 1) || N == (short_guid_form_length + 1), "String GUID of the form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} or XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX is expected");
if constexpr(N == (long_guid_form_length + 1))
{
if (str[0] != '{' || str[long_guid_form_length - 1] != '}')
throw std::domain_error{ "Missing opening or closing brace"s };
}
return make_guid_helper(str + (N == (long_guid_form_length + 1) ? 1 : 0));
}
}
using details::make_guid;
namespace literals
{
constexpr GUID operator "" _guid(const char *str, size_t N)
{
using namespace std::string_literals;
using namespace details;
if (!(N == long_guid_form_length || N == short_guid_form_length))
throw std::domain_error{ "String GUID of the form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} or XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX is expected"s };
if (N == long_guid_form_length && (str[0] != '{' || str[long_guid_form_length - 1] != '}'))
throw std::domain_error{ "Missing opening or closing brace"s };
return make_guid_helper(str + (N == long_guid_form_length ? 1 : 0));
}
}
}
@AlexBAV
Copy link
Author

AlexBAV commented Nov 24, 2017

Usage:

constexpr const GUID clsid = guid_parse::make_guid("{EFECF0A1-399E-40B8-A13C-ACE28DB40212}");

@AlexBAV
Copy link
Author

AlexBAV commented Nov 24, 2017

UDL Usage:

using namespace guid_parse::literals;

constexpr const GUID clsid = "{EFECF0A1-399E-40B8-A13C-ACE28DB40212}"_guid;

@Xeverous
Copy link

Xeverous commented Nov 27, 2017

I'm concerned by the amount of hardcoded magic integers, and multiple 36 - 39. This looks bad. DRY.

@GamePad64
Copy link

GamePad64 commented Nov 29, 2017

@dwcullop
Copy link

dwcullop commented Nov 13, 2018

Very nicely done. Thank you!

@tobias-loew
Copy link

tobias-loew commented Dec 21, 2018

Very nice. I used it to make a C++11 constexpr version (https://github.com/tobias-loew/constexpr-GUID-C-11-)

@tobias-loew
Copy link

tobias-loew commented Dec 21, 2018

I also added a make_uuid and ""_uuid to create boost-uuids. Do you have any objections if I propose this to the boost uuid library?

@LowLevelMahn
Copy link

LowLevelMahn commented Jul 24, 2020

I'm concerned by the amount of hardcoded magic integers, and multiple 36 - 39. This looks bad. DRY.

also validation is missing '-' can be replaced with anything, but still nice :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment