如何用constexpr在编译期填充CRC类内的C风格数组?
编译期生成CRC查表的实现方案
以下是修改后的完整代码,实现了编译期通过constexpr生成CRC表:
#include <concepts> #include <cstdint> // 辅助函数:constexpr反转无符号整数的位 template<typename T> constexpr T ReverseWord(T value) noexcept { T reversed = 0; for (size_t i = 0; i < sizeof(T) * 8; ++i) { reversed = (reversed << 1) | (value & 1); value >>= 1; } return reversed; } template<typename CrcBaseType, uint32_t Polynomial, uint32_t InitialValue, uint32_t XorOut, bool ReverseInput, bool ReverseOutput> requires std::unsigned_integral<CrcBaseType> class CrcDriver{ public: // 构造函数留空,表为静态编译期生成,无需初始化逻辑 CrcDriver() = default; // CRC计算接口示例(可根据需求扩展) constexpr CrcBaseType Calculate(const uint8_t* data, size_t length) const noexcept { CrcBaseType crc = static_cast<CrcBaseType>(InitialValue); if constexpr (ReverseInput) { for (size_t i = 0; i < length; ++i) { uint8_t reversed_byte = ReverseWord(data[i]); crc = (crc << 8) ^ table[reversed_byte ^ (static_cast<uint8_t>(crc >> (sizeof(CrcBaseType)*8 - 8)))]; } } else { for (size_t i = 0; i < length; ++i) { crc = (crc << 8) ^ table[data[i] ^ (static_cast<uint8_t>(crc >> (sizeof(CrcBaseType)*8 - 8)))]; } } if constexpr (ReverseOutput) { crc = ReverseWord(crc); } return crc ^ static_cast<CrcBaseType>(XorOut); } private: // 编译期确定类型字节数 static constexpr uint8_t size = sizeof(CrcBaseType); // 静态constexpr CRC表,编译期生成并初始化 static constexpr CrcBaseType table[256] = GenerateTable(); // 编译期生成正向CRC表 static constexpr auto GenerateForwardTable() noexcept { CrcBaseType result[256]{}; const uint32_t poly_mask = 0xFFFFFFFF >> (4 - size) * 8; const uint32_t top_bit = 0x80 << (8 * (size - 1)); for (uint32_t dividend = 0; dividend < 256; ++dividend) { uint32_t curByte = static_cast<uint32_t>(dividend) << (8 * (size - 1)); for (uint8_t bit = 0; bit < 8; ++bit) { if ((curByte & top_bit) != 0) { curByte = (curByte << 1) ^ (Polynomial & poly_mask); } else { curByte <<= 1; } } result[dividend] = static_cast<CrcBaseType>(curByte & poly_mask); } return result; } // 编译期生成反向CRC表 static constexpr auto GenerateReverseTable() noexcept { CrcBaseType result[256]{}; const uint32_t poly_mask = 0xFFFFFFFF >> (4 - size) * 8; // 反转多项式并截断到对应类型长度 const uint32_t reversed_poly = ReverseWord(static_cast<CrcBaseType>(Polynomial)) & poly_mask; for (uint32_t dividend = 0; dividend < 256; ++dividend) { uint32_t curByte = dividend; for (uint8_t bit = 0; bit < 8; ++bit) { if ((curByte & 0x01) != 0) { curByte = (curByte >> 1) ^ reversed_poly; } else { curByte >>= 1; } } result[dividend] = static_cast<CrcBaseType>(curByte & poly_mask); } return result; } // 根据ReverseInput参数选择表生成逻辑(编译期分支) static constexpr auto GenerateTable() noexcept { if constexpr (ReverseInput) { return GenerateReverseTable(); } else { return GenerateForwardTable(); } } };
核心改动说明
- 改用static constexpr存储表:每个模板实例的CRC表是固定的,静态成员避免重复存储,
constexpr强制表在编译期生成,完全无运行时初始化开销。 - 修复原代码逻辑缺陷:
- 移除了对模板参数
Polynomial的直接修改,改为用临时变量存储反转后的多项式。 - 添加位掩码处理,确保计算结果截断到
CrcBaseType的长度,避免溢出。
- 移除了对模板参数
- constexpr分支选择表类型:通过
constexpr if根据ReverseInput参数在编译期决定生成正向/反向表,无运行时分支判断。 - 实现constexpr位反转函数:满足反向CRC的位操作需求,全程在编译期执行。
编译期验证示例
可以通过以下代码确认表和CRC计算均在编译期完成:
// 标准CRC32参数实例化 using Crc32 = CrcDriver<uint32_t, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true>; // 编译期获取表中第一个元素 constexpr auto first_entry = Crc32::table[0]; // 编译期计算字符串"test"的CRC32值 constexpr uint32_t test_crc = Crc32{}.Calculate(reinterpret_cast<const uint8_t*>("test"), 4);
内容的提问来源于stack exchange,提问作者lazba
相关产品推荐
相关产品推荐

