如何基于std::variant创建编译期填充的解码函数数组
基于std::variant的编译期解码函数数组实现方案
1. 统一解码函数签名与基础实现
首先确保针对每个消息类型的解码函数特化正确,再包装成统一签名以适配variant输出:
#include <variant> #include <array> #include <cstdint> #include <stdexcept> // 前置声明消息类型 struct S1; struct S2; struct S3; struct S4; struct S5; // 定义最终的variant类型 using MsgVariant = std::variant<S1, S2, S3, S4, S5>; // 通用解码模板(仅作特化占位) template<typename T> T decode(const uint8_t* buffer, size_t len); // S1解码特化示例 template<> S1 decode<S1>(const uint8_t* buffer, size_t len) { S1 msg; // 此处写入S1的字节解析逻辑 return msg; } // S2-S5的解码特化同理实现...
定义统一的解码函数指针类型:
using DecodeFunc = MsgVariant(*)(const uint8_t*, size_t);
2. 绑定消息类型与MID
给每个消息类型添加唯一的静态MID常量:
struct S1 { static constexpr uint8_t mid = 0x01; // 消息字段定义... }; struct S2 { static constexpr uint8_t mid = 0x02; // ... }; // S3-S5依次定义各自的mid常量
3. 编译期生成解码函数数组
利用std::index_sequence遍历variant的类型列表,编译期生成函数指针数组:
// 包装函数:将返回具体类型的decode转为返回variant的统一接口 template<typename T> MsgVariant wrap_decode(const uint8_t* buffer, size_t len) { return decode<T>(buffer, len); } // 编译期生成函数数组的模板 template<std::size_t... Idxs> constexpr std::array<DecodeFunc, 5> make_decode_funcs(std::index_sequence<Idxs...>) { return { &wrap_decode<std::variant_alternative_t<Idxs, MsgVariant>>... }; } // 全局编译期初始化的解码函数数组 constexpr auto decode_funcs = make_decode_funcs(std::make_index_sequence<5>{});
4. MID到数组索引的映射
实现编译期的MID转索引逻辑,适配非连续MID的情况:
constexpr uint8_t mid_to_idx(uint8_t mid) { switch(mid) { case S1::mid: return 0; case S2::mid: return 1; case S3::mid: return 2; case S4::mid: return 3; case S5::mid: return 4; default: throw std::invalid_argument("Unknown message ID"); } }
5. 完整解码流程
MsgVariant decode_message(const uint8_t* buffer, size_t len) { if (len < 1) throw std::invalid_argument("Buffer too short for MID"); // 提取MID(假设MID占第一个字节) const uint8_t mid = buffer[0]; const auto idx = mid_to_idx(mid); // 调用对应解码函数,跳过MID字节 return decode_funcs[idx](buffer + 1, len - 1); }
核心说明
- 所有数组初始化和MID映射都在编译期完成,运行时仅做索引查找和函数调用,性能无损耗。
- 若需要支持更多消息类型,只需扩展
MsgVariant的类型列表、添加对应decode特化、更新make_decode_funcs的数组大小和mid_to_idx分支即可。 - 可添加
std::monostate到MsgVariant中,作为解码失败时的返回值,替代异常抛出(根据业务需求调整)。
内容的提问来源于stack exchange,提问作者fluter
相关产品推荐
相关产品推荐

