You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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里。我来给你梳理一下可行的解决方案:

核心思路

  1. 编译期过滤分隔符:先遍历字符串,跳过所有分隔符(默认是-,可以扩展为自定义分隔符),统计有效十六进制字符的数量。
  2. 编译期提取有效字符:实现一个函数,能精准定位到第n个有效十六进制字符,自动跳过中间的分隔符。
  3. 字符转字节:把每两个有效十六进制字符组合成一个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;
}

关键细节说明

  1. 编译期验证:所有字符合法性检查、分隔符过滤都是在编译期完成的,非法字符会直接触发编译器报错,完全不需要运行时开销。
  2. 奇数长度处理:默认对奇数个有效字符做高位补0处理,如果你需要严格要求偶数长度,只需要替换get_byte函数里的补0逻辑为static_assert即可。
  3. 扩展性:如果需要支持其他分隔符(比如:或_),可以把分隔符作为模板参数传入count_valid_hex_chars和get_valid_hex_char函数,轻松适配不同场景。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 11:43:04