C++创建结构体变量名动态数组报错,求实现JSON式遍历方案
解决结构体成员遍历与JSON转换的编译错误及实现方案
错误原因分析
你编写的config_t结构体中,auto T::*var的写法不合法:C++不允许用auto作为非静态数据成员的占位符,来声明指向不同类型结构体成员的指针。因为dataSave_t的成员nb(uint8_t类型)、state(bool类型)、dataIRArray(uint16_t[512]类型)对应的成员指针类型完全不同,无法统一存储在同类型的数组元素中,这就是编译报错的核心原因。
解决方案一:使用std::variant存储多类型成员指针(C++17+)
利用std::variant可以存储多种不同类型的特性,将成员指针封装成变体类型,再统一存入配置数组。同时结合std::visit实现对不同类型成员的遍历处理。
完整代码示例
#include <variant> #include <string> #include <iostream> // 示例使用nlohmann/json库做JSON转换,可替换为你实际使用的库 #include <nlohmann/json.hpp> using json = nlohmann::json; typedef struct dataSave { uint8_t nb; bool state; uint16_t dataIRArray[512]; } dataSave_t; dataSave_t mysave; // 定义支持的结构体成员指针类型变体 using DataSaveMemberPtr = std::variant< uint8_t dataSave_t::*, bool dataSave_t::*, uint16_t (dataSave_t::*)[512] >; struct config_t { DataSaveMemberPtr var; const char* name; }; // 配置列表:只需添加/移除此处的条目即可扩展处理的成员 const config_t config[] = { {&dataSave_t::nb, "nb"}, {&dataSave_t::state, "state"}, {&dataSave_t::dataIRArray, "dataIRArray"} }; // 将结构体序列化为JSON json serialize_data(const dataSave_t& data) { json j; for (const auto& cfg : config) { std::visit([&](auto ptr) { using MemberType = std::remove_pointer_t<decltype(ptr)>; if constexpr (std::is_array_v<MemberType>) { // 处理数组类型成员 j[cfg.name] = data.*ptr; } else { // 处理基础类型成员 j[cfg.name] = data.*ptr; } }, cfg.var); } return j; } // 从JSON反序列化到结构体 void deserialize_data(dataSave_t& data, const json& j) { for (const auto& cfg : config) { std::visit([&](auto ptr) { using MemberType = std::remove_pointer_t<decltype(ptr)>; if constexpr (std::is_array_v<MemberType>) { // 处理数组类型成员 j.at(cfg.name).get_to(data.*ptr); } else { // 处理基础类型成员 data.*ptr = j.at(cfg.name).get<decltype(data.*ptr)>(); } }, cfg.var); } } int main() { // 测试序列化 mysave.nb = 10; mysave.state = true; for (int i = 0; i < 5; ++i) mysave.dataIRArray[i] = i * 2; json j = serialize_data(mysave); std::cout << "Serialized JSON:\n" << j.dump(4) << std::endl; // 测试反序列化 dataSave_t new_data; deserialize_data(new_data, j); std::cout << "\nDeserialized data:\nnb: " << (int)new_data.nb << "\nstate: " << std::boolalpha << new_data.state << "\ndataIRArray[0]: " << new_data.dataIRArray[0] << std::endl; return 0; }
解决方案二:使用类型擦除(兼容C++11及以上)
如果你的开发环境不支持C++17,可以通过抽象基类+模板派生类的方式实现类型擦除,将不同成员的访问逻辑封装成统一的接口。
完整代码示例
#include <string> #include <iostream> #include <vector> #include <nlohmann/json.hpp> using json = nlohmann::json; typedef struct dataSave { uint8_t nb; bool state; uint16_t dataIRArray[512]; } dataSave_t; dataSave_t mysave; // 抽象基类:定义统一的序列化/反序列化接口 struct ConfigBase { virtual ~ConfigBase() = default; virtual void serialize(const dataSave_t& data, json& j) const = 0; virtual void deserialize(dataSave_t& data, const json& j) const = 0; }; // 模板派生类:处理具体类型的结构体成员 template<typename MemberType> struct ConfigItem : public ConfigBase { MemberType dataSave_t::*ptr; const char* name; ConfigItem(MemberType dataSave_t::*p, const char* n) : ptr(p), name(n) {} void serialize(const dataSave_t& data, json& j) const override { if constexpr (std::is_array_v<MemberType>) { j[name] = data.*ptr; } else { j[name] = data.*ptr; } } void deserialize(dataSave_t& data, const json& j) const override { if constexpr (std::is_array_v<MemberType>) { j.at(name).get_to(data.*ptr); } else { data.*ptr = j.at(name).get<decltype(data.*ptr)>(); } } }; // 配置列表:只需添加/移除此处的条目即可扩展处理的成员 const std::vector<ConfigBase*> config = { new ConfigItem<uint8_t>(&dataSave_t::nb, "nb"), new ConfigItem<bool>(&dataSave_t::state, "state"), new ConfigItem<uint16_t[512]>(&dataSave_t::dataIRArray, "dataIRArray") }; // 序列化函数 json serialize_data(const dataSave_t& data) { json j; for (const auto* cfg : config) { cfg->serialize(data, j); } return j; } // 反序列化函数 void deserialize_data(dataSave_t& data, const json& j) { for (const auto* cfg : config) { cfg->deserialize(data, j); } } // 清理配置列表内存(若使用智能指针可省略此步骤) void cleanup_config() { for (auto* cfg : config) { delete cfg; } } int main() { mysave.nb = 20; mysave.state = false; for (int i = 0; i < 5; ++i) mysave.dataIRArray[i] = i * 3; json j = serialize_data(mysave); std::cout << "Serialized JSON:\n" << j.dump(4) << std::endl; dataSave_t new_data; deserialize_data(new_data, j); std::cout << "\nDeserialized data:\nnb: " << (int)new_data.nb << "\nstate: " << std::boolalpha << new_data.state << "\ndataIRArray[1]: " << new_data.dataIRArray[1] << std::endl; cleanup_config(); return 0; }
使用说明
- 两种方案都实现了只需修改配置列表即可添加/移除结构体成员的JSON转换逻辑,完全符合你的需求。
- 方案一需要编译器支持C++17(如GCC 7+、Clang 5+、MSVC 2017+),代码更简洁。
- 方案二兼容C++11及以上,若想简化内存管理,可将
ConfigBase*替换为std::unique_ptr<ConfigBase>。 - 示例中使用了nlohmann/json库,你可以替换为其他JSON库,只需调整序列化/反序列化的具体实现逻辑。
内容的提问来源于stack exchange,提问作者Jvinicius06
相关产品推荐
相关产品推荐

