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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 09:50:17