Created
December 9, 2021 23:45
-
-
Save adrianmgg/f80d607d7a408da2051132e455924f10 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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