如何通过成员变量表简化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; }
关键说明
- 类型安全与顺序一致性:成员描述表是编译期元组,每个成员的类型严格匹配,遍历顺序完全由元组的定义顺序决定,
processthis和processthat会严格遵循同一顺序,彻底避免手动编写时的顺序错误。 - 解决类型适配问题:无需
std::bind/std::function的类型擦除,通过模板折叠表达式直接处理每个成员的原生类型,无运行时开销。 - 扩展性:新增结构体时,仅需添加对应的成员描述表,无需修改通用处理函数。
简化技巧:宏定义减少重复代码
如果成员数量较多,可以用宏简化成员描述的编写:
#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
相关产品推荐
相关产品推荐

