C++17下实现支持分隔符的十六进制字符串字面量到std::array<std::uint8_t, Size>的constexpr解析器
C++17下实现支持分隔符的十六进制字符串字面量到std::array<std::uint8_t, Size>的constexpr解析器
嘿,我懂你现在的困扰——原来的constexpr解析器只能处理连续的十六进制字符串,但现在需要支持带分隔符(比如UUID里的-)的通用场景,而且还得死死卡在C17上,不能用C20的consteval对吧?其实核心问题就是在编译期跳过那些分隔符,同时准确收集有效的十六进制字符,再把它们两两组合成uint8_t塞进std::array里。我来给你梳理一下可行的解决方案:
核心思路
- 编译期过滤分隔符:先遍历字符串,跳过所有分隔符(默认是
-,可以扩展为自定义分隔符),统计有效十六进制字符的数量。 - 编译期提取有效字符:实现一个函数,能精准定位到第n个有效十六进制字符,自动跳过中间的分隔符。
- 字符转字节:把每两个有效十六进制字符组合成一个
uint8_t,同时处理有效字符数为奇数的情况(默认高位补0,也可以改为编译期报错)。
完整实现代码
#include <array> #include <vector> #include <utility> // for index_sequence namespace _internal { // 编译期统计有效十六进制字符数(跳过分隔符) constexpr std::size_t count_valid_hex_chars(const char* str, std::size_t len) { std::size_t count = 0; for (std::size_t i = 0; i < len; ++i) { const char c = str[i]; if (c == '\0') break; if (c == '-') continue; // 可替换为模板参数支持自定义分隔符 // 编译期验证字符合法性 static_assert( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'), "Invalid hex character in string literal" ); ++count; } return count; } // 编译期获取第n个有效十六进制字符(自动跳过分隔符) constexpr char get_valid_hex_char(const char* str, std::size_t len, std::size_t idx) { std::size_t current = 0; for (std::size_t i = 0; i < len; ++i) { const char c = str[i]; if (c == '\0') break; if (c == '-') continue; if (current == idx) { return c; } ++current; } return '\0'; // 编译期会触发错误如果idx超出有效范围 } // 将两个十六进制字符转换为一个uint8_t constexpr std::uint8_t hex_pair_to_byte(char high, char low) { auto nibble = [](char c) constexpr -> std::uint8_t { if (c >= '0' && c <= '9') return static_cast<std::uint8_t>(c - '0'); if (c >= 'a' && c <= 'f') return static_cast<std::uint8_t>(c - 'a' + 10); return static_cast<std::uint8_t>(c - 'A' + 10); }; return (nibble(high) << 4) | nibble(low); } // 根据数组索引获取对应的字节,处理奇数长度的情况 template <std::size_t StrSize, std::size_t ArrIdx> constexpr std::uint8_t get_byte(const char (&str)[StrSize]) { constexpr auto valid_count = count_valid_hex_chars(str, StrSize); constexpr auto byte_idx = ArrIdx * 2; if constexpr (byte_idx + 1 < valid_count) { const char high = get_valid_hex_char(str, StrSize, byte_idx); const char low = get_valid_hex_char(str, StrSize, byte_idx + 1); return hex_pair_to_byte(high, low); } else { // 奇数长度:最后一个字符作为低位,高位补0 const char low = get_valid_hex_char(str, StrSize, byte_idx); return hex_pair_to_byte('0', low); // 如果要求严格偶数长度,替换为下面的断言: // static_assert(valid_count % 2 == 0, "Hex string must have even number of valid characters"); // return 0; } } // 填充std::array的核心函数 template <std::size_t StrSize, std::size_t... Is> constexpr auto FillArray(const char (&str)[StrSize], std::index_sequence<Is...>) { constexpr auto valid_count = count_valid_hex_chars(str, StrSize); constexpr auto arr_size = (valid_count + 1) / 2; // 向上取整处理奇数长度 return std::array<std::uint8_t, arr_size>{get_byte<StrSize, Is>(str)...}; } } // namespace _internal // 封装结果的辅助结构体,支持转std::array和std::vector template <std::size_t N> struct LiteralArray { std::array<std::uint8_t, N> value_{}; constexpr explicit LiteralArray(std::array<std::uint8_t, N> value) : value_{std::move(value)} {} constexpr operator std::array<std::uint8_t, N>() const { return value_; } operator std::vector<std::uint8_t>() const { return {value_.begin(), value_.end()}; } }; // 对外暴露的入口函数 template <std::size_t Size> constexpr auto MakeLiteralArray(const char (&str)[Size]) { constexpr auto valid_count = _internal::count_valid_hex_chars(str, Size); constexpr auto arr_size = (valid_count + 1) / 2; return LiteralArray<arr_size>{ _internal::FillArray<Size>(str, std::make_index_sequence<arr_size>{}) }; } // 测试用例 int main() { // 测试UUID格式 constexpr auto uuid_arr = MakeLiteralArray("f81d4fae-7dec-11d0-a765-00a0c91e6bf6"); static_assert(uuid_arr[0] == 0xF8); static_assert(uuid_arr[1] == 0x1D); static_assert(uuid_arr[4] == 0x7D); static_assert(uuid_arr[9] == 0x6B); // 测试短分隔符字符串 constexpr auto short_arr = MakeLiteralArray("af1c-dc09"); static_assert(short_arr[0] == 0xAF); static_assert(short_arr[2] == 0xDC); // 测试奇数长度的情况 constexpr auto odd_arr = MakeLiteralArray("55-ffffff"); static_assert(odd_arr[0] == 0x55); static_assert(odd_arr[3] == 0x0F); // 最后一个字符'f'补0高位得到0x0F return 0; }
关键细节说明
- 编译期验证:所有字符合法性检查、分隔符过滤都是在编译期完成的,非法字符会直接触发编译器报错,完全不需要运行时开销。
- 奇数长度处理:默认对奇数个有效字符做高位补0处理,如果你需要严格要求偶数长度,只需要替换
get_byte函数里的补0逻辑为static_assert即可。 - 扩展性:如果需要支持其他分隔符(比如
:或_),可以把分隔符作为模板参数传入count_valid_hex_chars和get_valid_hex_char函数,轻松适配不同场景。
内容来源于stack exchange
相关产品推荐
相关产品推荐

