Skip to content

Instantly share code, notes, and snippets.

@adrianmgg
Created December 9, 2021 23:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adrianmgg/f80d607d7a408da2051132e455924f10 to your computer and use it in GitHub Desktop.
Save adrianmgg/f80d607d7a408da2051132e455924f10 to your computer and use it in GitHub Desktop.
/*
user-defined string literal operator to parse windows api GUIDs from strings at compile time.
any of these will work:
- "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"_guid
- "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"_guid
- "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"_guid
*/
#pragma once
#ifndef __GUID_LITERAL_H__
#define __GUID_LITERAL_H__
#include "guiddef.h"
#include <cstdint> // uint8_t
#include <string_view> // string_view
#include <stdexcept> // invalid_argument
#include <type_traits> // is_integral
namespace {
constexpr std::uint8_t parse_hex_char(const char c) {
if('0' <= c && c <= '9') return 0 + (c - '0');
if('a' <= c && c <= 'f') return 10 + (c - 'a');
if('A' <= c && c <= 'F') return 10 + (c - 'A');
throw std::invalid_argument("invalid hex char"); // won't actually see this error since you'll just get a 'this isn't a constant expression' error instead, but eh good enough
}
template<typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
constexpr T parse_hex(const std::string_view& s) {
// TODO check that T is big enough?
T ret = 0;
for(const char c : s) {
ret <<= 4;
ret |= parse_hex_char(c);
}
return ret;
}
constexpr GUID parse_guid_chunk(const std::string_view& chunk1, const std::string_view& chunk2, const std::string_view& chunk3, const std::string_view& chunk4, const std::string_view& chunk5) {
#define PARSE_GUID_CHUNK_LENCHECK(num, len) if(chunk##num.length() != len) throw std::invalid_argument("guid chunk " #num " must have length " #len ".");
PARSE_GUID_CHUNK_LENCHECK(1, 8)
PARSE_GUID_CHUNK_LENCHECK(2, 4)
PARSE_GUID_CHUNK_LENCHECK(3, 4)
PARSE_GUID_CHUNK_LENCHECK(4, 4)
PARSE_GUID_CHUNK_LENCHECK(5, 12)
#undef PARSE_GUID_CHUNK_LENCHECK
return {
parse_hex<std::uint32_t>(chunk1),
parse_hex<std::uint16_t>(chunk2),
parse_hex<std::uint16_t>(chunk3),
{
parse_hex<std::uint8_t>(chunk4.substr(0, 2)),
parse_hex<std::uint8_t>(chunk4.substr(2, 2)),
parse_hex<std::uint8_t>(chunk5.substr(0, 2)),
parse_hex<std::uint8_t>(chunk5.substr(2, 2)),
parse_hex<std::uint8_t>(chunk5.substr(4, 2)),
parse_hex<std::uint8_t>(chunk5.substr(6, 2)),
parse_hex<std::uint8_t>(chunk5.substr(8, 2)),
parse_hex<std::uint8_t>(chunk5.substr(10, 2))
}
};
}
constexpr GUID parse_guid(const std::string_view& str) {
if(str.length() == 38) { // "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
if(str[0] != '{' || str[37] != '}') throw std::invalid_argument("expected guid string of this length to be wrapped in {}s");
return parse_guid(str.substr(1, 36));
}
else if(str.length() == 36) { // "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
if(str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-') throw std::invalid_argument("expected guid string of this length to be hyphen-delimited");
return parse_guid_chunk(str.substr(0, 8), str.substr(9, 4), str.substr(14, 4), str.substr(19, 4), str.substr(24, 12));
}
else if(str.length() == 32) { // "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
return parse_guid_chunk(str.substr(0, 8), str.substr(8, 4), str.substr(12, 4), str.substr(16, 4), str.substr(20, 12));
}
else throw std::invalid_argument("not sure how to parse guid string with that length");
}
}
constexpr GUID operator"" _guid(const char* cstr, const std::size_t len) {
return parse_guid(std::string_view(cstr, len));
}
// tests
namespace {
constexpr bool guid_compare(const GUID& a, const GUID& b) {
return true
&& (a.Data1 == b.Data1)
&& (a.Data2 == b.Data2)
&& (a.Data3 == b.Data3)
&& (a.Data4[0] == b.Data4[0])
&& (a.Data4[1] == b.Data4[1])
&& (a.Data4[2] == b.Data4[2])
&& (a.Data4[3] == b.Data4[3])
&& (a.Data4[4] == b.Data4[4])
&& (a.Data4[5] == b.Data4[5])
&& (a.Data4[6] == b.Data4[6])
&& (a.Data4[7] == b.Data4[7]);
}
static_assert(guid_compare(
{ 0xec3da6e1, 0xc80f, 0x45ec, { 0xa9, 0x68, 0x74, 0xc8, 0xdc, 0xb4, 0xa1, 0x9a } },
"{EC3DA6E1-C80F-45EC-A968-74C8DCB4A19A}"_guid
));
static_assert(guid_compare(
{ 0xec3da6e1, 0xc80f, 0x45ec, { 0xa9, 0x68, 0x74, 0xc8, 0xdc, 0xb4, 0xa1, 0x9a } },
"EC3DA6E1-C80F-45EC-A968-74C8DCB4A19A"_guid
));
static_assert(guid_compare(
{ 0xec3da6e1, 0xc80f, 0x45ec, { 0xa9, 0x68, 0x74, 0xc8, 0xdc, 0xb4, 0xa1, 0x9a } },
"EC3DA6E1C80F45ECA96874C8DCB4A19A"_guid
));
}
#endif // __GUID_LITERAL_H__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment