【“宏孩儿”入门】通过宏定义将类型和函数结构解耦

2023-10-17 16:38:49 浏览数 (1)

想象这样一个场景,我们有一个表结构体StudentInfo,同时用数组存储它作为一张表,我们需要提供一套根据Key来增删改查的函数操作这张表。 (这里可能有聪明的小伙伴会想到为什么不用哈希表来存储,这样不就自带一套根据Key来增删改查函数了吗? 是的一般情况下可以这样,但也有些情景下不能使用哈希表,比如UE中TSet不支持同步。) 但如果每有一张这样的表我们都要写一套增删改查函数未免太累了。 我们仔细观察增删改查函数时会发现,除了操作的表,key不同外,代码结构上是相同的,那怎么让表和函数结构解耦呢? 因为表可能是私有成员同时还需要用到表的key,所以用模板还不太好解决。

可以考虑用宏来做。 比如在某个模块中有多张表:

代码语言:javascript复制
struct StudentInfo{
    int Id;
    std::string Val1;
    float Val2;
};

struct Fish{
    //...
}
//...

class Module{
//...
private:
    //SaveGame
    std::vector<StudentInfo> StudentInfoTable;
    std::vector<FishInfo> FishInfoTable;
    //其他表...
public:
    //各表的增删改查函数...
}

比如我们想为所有表提供一个Has函数,可以写个宏: (如果对##的作用不了解可以点击看这篇文章)

代码语言:javascript复制
#define DECLARE_DATA_TABLE_HAS_METHOD(DataArray, KeyType, KeyField) 
bool DataArray##HasKey(KeyType FindBy##KeyField){                   
    bool IsHas = false;                                             
    for(const auto &Row : DataArray){                               
        if(Row.KeyField == FindBy##KeyField){                       
            IsHas = true;                                           
            break;                                                  
        }                                                           
    }                                                               
    return IsHas;                                                   
}                                                                   

(这里不讨论实现性能问题,那不是这篇文章的主题) 然后可以在需要定义has函数的模块中加上这个宏传入表和key信息就等于生成了一个该表的Has函数:

代码语言:javascript复制
DECLARE_DATA_TABLE_CRUD_METHOD(StudentInfoTable, StudentInfo, int, Id)

等价于

代码语言:javascript复制
bool StudentInfoTableHasKey(KeyType FindById){
    //...
}

接下来就可以举一反三写出生成其它函数的宏,同时我们还可以写个宏来专门负责打包这些宏:

代码语言:javascript复制
//生成数据表基础的增删改查方法
#define DECLARE_DATA_TABLE_CRUD_METHOD(DataArray, RowType, KeyType, KeyField) 
DECLARE_DATA_TABLE_HAS_METHOD(DataArray, KeyType, KeyField)
DECLARE_DATA_TABLE_FIND_METHOD(DataArray, RowType, KeyType, KeyField)
DECLARE_DATA_TABLE_FIND_OR_ADD_METHOD(DataArray, RowType, KeyType, KeyField)
//...

这样我们为每张表写增删改查函数就只需要一条宏:

代码语言:javascript复制
DECLARE_DATA_TABLE_CRUD_METHOD(StudentInfoTable, StudentInfo, int, Id)

完整样例代码

代码语言:javascript复制
#include<iostream>
#include<string>
#include<vector>

#define DECLARE_DATA_TABLE_HAS_METHOD(DataArray, KeyType, KeyField) 
bool DataArray##HasKey(KeyType FindBy##KeyField){                   
    bool IsHas = false;                                             
    for(const auto &Row : DataArray){                               
        if(Row.KeyField == FindBy##KeyField){                       
            IsHas = true;                                           
            break;                                                  
        }                                                           
    }                                                               
    return IsHas;                                                   
}                                                                   

#define DECLARE_DATA_TABLE_FIND_METHOD(DataArray, RowType, KeyType, KeyField)
RowType *DataArray##FindRow(KeyType FindBy##KeyField){
    RowType *Ret = nullptr;
    for(auto &Row : DataArray){
        if(Row.KeyField == FindBy##KeyField){
            Ret = &Row;
            break;
        }
    }
    return Ret;
}

//前置依赖:Find
#define DECLARE_DATA_TABLE_FIND_OR_ADD_METHOD(DataArray, RowType, KeyType, KeyField)  
RowType &DataArray##FindOrAddRow(KeyType In##KeyField){
    RowType *FindRow = DataArray##FindRow(In##KeyField);
    if(FindRow == nullptr){
        RowType Row;
        Row.KeyField = In##KeyField;
        DataArray.push_back(Row);
        FindRow = &DataArray[DataArray.size()-1];
    }
    return *FindRow;
}


//生成数据表基础的增删改查方法
#define DECLARE_DATA_TABLE_CRUD_METHOD(DataArray, RowType, KeyType, KeyField) 
DECLARE_DATA_TABLE_HAS_METHOD(DataArray, KeyType, KeyField)
DECLARE_DATA_TABLE_FIND_METHOD(DataArray, RowType, KeyType, KeyField)
DECLARE_DATA_TABLE_FIND_OR_ADD_METHOD(DataArray, RowType, KeyType, KeyField)



struct StudentInfo{
    int Id;
    std::string Val1;
    float Val2;
};

class Module{
//...
private:
    //SaveGame
    std::vector<StudentInfo> StudentInfoTable;
public:

    DECLARE_DATA_TABLE_CRUD_METHOD(StudentInfoTable, StudentInfo, int, Id)
    /*
    DECLARE_DATA_TABLE_HAS_METHOD(StudentInfoTable, int, Id);
    DECLARE_DATA_TABLE_FIND_METHOD(StudentInfoTable, StudentInfo, int, Id);
    DECLARE_DATA_TABLE_FIND_OR_ADD_METHOD(StudentInfoTable, StudentInfo, int, Id);
    */
};

int main(){
    Module NewModule;
    std::cout << NewModule.StudentInfoTableHasKey(5) << "n";
    std::cout << NewModule.StudentInfoTableFindRow(5) << "n";
    StudentInfo &FindStudentInfo = NewModule.StudentInfoTableFindOrAddRow(2001);
    FindStudentInfo.Val1 = "zsd";
    FindStudentInfo.Val2 = 1.5f;
    std::cout << NewModule.StudentInfoTableFindOrAddRow(2001).Val2 << "n";

    return 0;
}

0 人点赞