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

如何通过成员变量表简化C++重复结构体处理代码?

基于编译期成员描述表实现通用结构体处理函数(C++20)

核心思路

利用C++20的std::apply和模板元编程,将每个结构体的成员信息(名称、成员指针)封装为编译期元组,通过统一的遍历逻辑实现processthis/processthat的通用化,确保成员处理顺序完全一致,无需重复编写结构体专属处理代码。

完整代码实现

1. 定义成员描述模板

#include <tuple>
#include <string_view>
#include <utility>

// 单个成员的描述模板:存储成员名称(编译期字符串)和成员指针
template <typename StructType, typename MemberType>
struct MemberDescriptor {
    constexpr static std::string_view name;
    constexpr static MemberType StructType::* ptr;
};

// 为MemberDescriptor添加constexpr构造函数(C++20支持)
template <typename S, typename M>
constexpr MemberDescriptor<S, M> make_member_desc(std::string_view name, M S::* ptr) {
    return {name, ptr};
}

2. 定义原始结构体及对应成员描述表

// 原始结构体(不可修改)
struct DataA {
    int a;
    std::string b;
    double c;
};

struct DataB {
    bool one;
    long long two;
};

// DataA的成员描述表:仅需维护一次成员顺序
struct DataADefinition {
    constexpr static auto members = std::make_tuple(
        make_member_desc<DataA>("a", &DataA::a),
        make_member_desc<DataA>("b", &DataA::b),
        make_member_desc<DataA>("c", &DataA::c)
    );
};

// DataB的成员描述表
struct DataBDefinition {
    constexpr static auto members = std::make_tuple(
        make_member_desc<DataB>("one", &DataB::one),
        make_member_desc<DataB>("two", &DataB::two)
    );
};

3. 通用处理函数实现

// 假设的业务处理函数(示例)
void dothis(int index, auto& member) {
    // 这里写processthis的具体逻辑,比如打印成员信息
    printf("processthis: index=%d, value=%g\n", index, member);
}

void dothat(int index, auto& member) {
    // 这里写processthat的具体逻辑
    printf("processthat: index=%d, value=%g\n", index, member);
}

// 通用processthis:遍历成员描述表处理每个成员
template <typename StructType, typename Definition>
void processthis(StructType& data) {
    int index = 0;
    // 用std::apply遍历元组,折叠表达式展开处理每个成员
    std::apply([&](auto&&... desc) {
        (dothis(++index, data.*(desc.ptr)), ...);
    }, Definition::members);
}

// 通用processthat:逻辑与processthis完全一致,仅调用的业务函数不同
template <typename StructType, typename Definition>
void processthat(StructType& data) {
    int index = 0;
    std::apply([&](auto&&... desc) {
        (dothat(++index, data.*(desc.ptr)), ...);
    }, Definition::members);
}

4. 使用示例

#include <iostream>

int main() {
    DataA a_data{42, "test", 3.1415};
    processthis<DataA, DataADefinition>(a_data);
    processthat<DataA, DataADefinition>(a_data);

    DataB b_data{true, 123456789};
    processthis<DataB, DataBDefinition>(b_data);
    processthat<DataB, DataBDefinition>(b_data);

    return 0;
}

关键说明

  1. 类型安全与顺序一致性:成员描述表是编译期元组,每个成员的类型严格匹配,遍历顺序完全由元组的定义顺序决定,processthis和processthat会严格遵循同一顺序,彻底避免手动编写时的顺序错误。
  2. 解决类型适配问题:无需std::bind/std::function的类型擦除,通过模板折叠表达式直接处理每个成员的原生类型,无运行时开销。
  3. 扩展性:新增结构体时,仅需添加对应的成员描述表,无需修改通用处理函数。

简化技巧:宏定义减少重复代码

如果成员数量较多,可以用宏简化成员描述的编写:

#define DEF_MEMBER(Struct, Name) make_member_desc<Struct>(#Name, &Struct::Name)

// 修改DataADefinition为:
struct DataADefinition {
    constexpr static auto members = std::make_tuple(
        DEF_MEMBER(DataA, a),
        DEF_MEMBER(DataA, b),
        DEF_MEMBER(DataA, c)
    );
};

内容的提问来源于stack exchange,提问作者GKarRacer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 07:20:57