如何用boost::pfr::for_each_field_with_name实现哈希表转嵌套结构体?
实现方案:从
std::unordered_map<std::string, double>还原嵌套结构体 基于你已用boost::pfr::for_each_field_with_name实现的序列化逻辑,逆向的from_hashmap可以利用同样的反射能力,通过路径解析+递归遍历结构体字段完成还原。核心思路是将哈希表中带层级的键(比如"addr.lat")拆分为字段路径,逐层定位到结构体中的目标字段并赋值。
步骤1:路径拆分辅助函数
首先需要把哈希表的键(嵌套路径字符串)拆分为层级字段名列表,比如把"person.addr.lng"拆成{"person", "addr", "lng"}:
#include <vector> #include <string> #include <sstream> std::vector<std::string> split_path(const std::string& path) { std::vector<std::string> parts; std::stringstream ss(path); std::string part; while (std::getline(ss, part, '.')) { if (!part.empty()) { parts.push_back(part); } } return parts; }
步骤2:递归字段定位与赋值实现
编写递归函数,从根结构体开始,根据路径层级逐层匹配字段名:
- 如果是最后一层路径,直接给
double字段赋值 - 如果是中间层级,找到对应的嵌套结构体字段,递归进入下一层
#include <boost/pfr.hpp> #include <unordered_map> #include <stdexcept> namespace detail { // 递归终止条件:处理double类型字段 template <typename T> std::enable_if_t<std::is_same_v<T, double>> assign_field(T& field, const std::vector<std::string>& path, size_t idx, const std::unordered_map<std::string, double>& hashmap) { if (idx != path.size() - 1) { throw std::invalid_argument("Invalid path for double field"); } // 拼接完整路径作为键查找 std::string full_path; for (size_t i = 0; i < path.size(); ++i) { if (i > 0) full_path += "."; full_path += path[i]; } auto it = hashmap.find(full_path); if (it == hashmap.end()) { throw std::out_of_range("Field not found in hashmap: " + full_path); } field = it->second; } // 递归处理结构体类型字段 template <typename T> std::enable_if_t<!std::is_same_v<T, double>> assign_field(T& struct_obj, const std::vector<std::string>& path, size_t idx, const std::unordered_map<std::string, double>& hashmap) { if (idx >= path.size()) { throw std::invalid_argument("Path exceeds struct depth"); } const std::string& current_field_name = path[idx]; bool field_found = false; // 遍历当前结构体的所有字段,匹配名称 boost::pfr::for_each_field_with_name(struct_obj, [&](auto& field, const char* name) { if (current_field_name == name) { assign_field(field, path, idx + 1, hashmap); field_found = true; } }); if (!field_found) { throw std::out_of_range("Struct field not found: " + current_field_name); } } } // 对外接口函数 template <typename Struct> void from_hashmap(Struct& obj, const std::unordered_map<std::string, double>& hashmap) { // 遍历哈希表的每个键值对,逐个还原字段 for (const auto& [path_str, value] : hashmap) { auto path_parts = split_path(path_str); if (path_parts.empty()) continue; detail::assign_field(obj, path_parts, 0, hashmap); } }
步骤3:使用示例
假设你的Person结构体定义如下:
struct Address { double lat; double lng; }; struct Person { double age; Address addr; };
还原代码示例:
#include <iostream> int main() { std::unordered_map<std::string, double> hashmap = { {"age", 30.0}, {"addr.lat", 40.7128}, {"addr.lng", -74.0060} }; Person p; try { from_hashmap(p, hashmap); std::cout << "Age: " << p.age << "\n"; std::cout << "Lat: " << p.addr.lat << ", Lng: " << p.addr.lng << "\n"; } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << "\n"; } return 0; }
关键说明
- 反射能力依赖:完全复用
boost::pfr的编译期反射,不需要手动编写字段映射,支持任意层级的嵌套结构体(只要所有最终字段都是double) - 错误处理:加入了路径无效、字段不存在等异常抛出,你可以根据需求改为返回
bool或其他错误处理方式 - 路径匹配:严格按照哈希表键的层级路径匹配结构体字段,确保序列化和反序列化的一致性
内容的提问来源于stack exchange,提问作者Tom McLean
相关产品推荐
相关产品推荐

