程式碼自動生成工具(一)-Csv讀表程式碼自動生成工具
之前提到了自定義的Csv格式的表格讀取的一個工具類CsvReader
這裡我們實現一個可以對任意符合我們人為規定的格式的Csv檔案,自動生成其對應的讀表程式碼
本工具需要boost庫支援,本人用的是1.55.0
這裡首先定義Csv中支援的幾種列型別:FieldType:包括有/無符號整數,小數,字串,子結構
enum FieldType
{
FT_int = 0, //整數
FT_uint, //整數
FT_float, //浮點數
FT_string, //字串
FT_struct, //子結構
FT_unknow,
};
列型別為子結構時,對於子結構資訊的定義:ChildStruct:包括欄位名,欄位型別
class ChildStruct { public: ChildStruct(); ChildStruct(const ChildStruct &other); ChildStruct& operator = (const ChildStruct &other); ~ChildStruct(); std::vector<std::string> m_arrChildName; // 子結構的欄位名稱 std::vector<FieldType> m_arrFieldType; // 子結構的欄位型別 };
整個表格檔案,前3行涉及的資訊的定義:TableFile:包括表名稱,列名稱,列型別,註釋,是否是主鍵,是否是陣列
class CsvFile { public: CsvFile(); CsvFile(const CsvFile &other); CsvFile& operator = (const CsvFile &other); ~CsvFile(); std::string m_strClassName; // 類名稱 std::vector<std::string> m_arrName; // 列名稱(原始名稱) std::vector<std::string> m_arrType; // 列型別標識字串 std::vector<std::string> m_arrComments; // 列註釋 std::vector<int> m_arrKey; // 主鍵列下標 std::vector<bool> m_arrIsList; // 列是否是不定長陣列(lst) std::vector<FieldType> m_arrFieldType; // 列型別列舉 std::map<int, ChildStruct> m_mapChildStruct;// 巢狀的子結構欄位名稱,型別對映表 };
Csv檔案解析類,讀取檔案文字內容,按相應配置(名稱空間,檔案路徑等),解析生成相應的CsvFile物件,
class CsvParse { public: CsvParse(); ~CsvParse(); bool ParseFile(const std::string& fullPathName); // 解析完畢的csv檔案資訊列表 std::map<std::string, CsvFile> m_Files; // csv檔案解析工具輸入引數 public: std::string m_NameSpace; // 自定義名稱空間 bool m_ThreadSpecific; // 是否需要執行緒本地表格資料 std::vector<std::string> m_CsvFileName; // 待處理的csv檔案列表 std::string m_LoaderPath; // loader輸出cpp程式碼路徑 std::string m_HelperPath; // helper輸出cpp程式碼路徑 };
傳入一個Csv的檔案完整路徑,呼叫CsvReader,讀取出列名稱,列型別,列註釋
對於列名稱,呼叫StringTool進行首字母大寫
對於列型別,按照自定義Csv格式解析出對應型別或子結構
對於列註釋,如果是Utf8,則呼叫StringTool轉換成GBK
具體的程式碼生成包含兩部分功能:
void ParseCsv2LoaderH(const CsvParse& csvParse)
void ParseCsv2LoaderCpp(const CsvParse& csvParse)
void ParseCsv2HelperH(const CsvParse& csvParse)
void ParseCsv2HelperCpp(const CsvParse& csvParse)
傳入一個解析好的CsvParse物件,生成具體程式碼
Loader生成對應的讀取程式碼,讀取csv檔案的每一行資料,轉成相應的資料結構。
Helper生成一個空的輔助類,用於需要對資料重新組織的情況,比如配置中是按某個id列作為主鍵,存成一個map,提供查詢,但是實際邏輯中還需要按某個名稱列作為索引,再提供一個map,用於提供按名稱查詢(或者模糊查詢),這時候就需要手動再提供一個新的查詢函式,就可以寫在helper中
需要生成的程式碼包括:
// 1、標頭檔案:map,vector等
//
// 2、名稱空間(由程式執行引數決定)
//
// 3、每個子結構:class XXX {...};
// 包括:
// 構造:XXX() {...}
// 拷貝構造:XXX(const XXX &other) {...}
// 賦值:XXX& operator = (const XXX &other) {...}
// 賦值:XXX& operator = (const std::string &other) {...}
// 子結構的各個欄位羅列
//
// 4、表結構:class XXX {...};
// 包括:
// 構造:XXX() {...}
// 拷貝構造:XXX(const XXX &other) {...}
// 賦值:XXX& operator = (const XXX &other) {...}
// 表結構的各個欄位羅列
//
// 5、表結構對應的管理器類:class XXXTable {...};
// 包括:
// 構造:XXXTable();
// 析構:~XXXTable();
// 獲取表格資料:Get
// 對於3個主鍵的,則生成:
// const XXX* Get(k1, k2, k3){...}
// const std::map<k3type, XXX>* Get(k1, k2){...}
// const std::map<k2type, std::map<k3type, XXX> >* Get(k1){...}
// const std::map<k1type, std::map<k2type, std::map<k3type, XXX> > >& Get(){...}
// 對於2個主鍵的則生成:
// const XXX* Get(k1, k2);
// const std::map<k2type, XXX>* Get(k1);
// const std::map<k1type, std::map<k2type, XXX> >& Get();
// 對於1個主鍵的則生成:
// const XXX* Get(k1);
// const std::map<k1type, XXX>& Get();
// 對於沒有主鍵的,那麼按照陣列處理,生成:
// const XXX* Get(index);
// const std::vector<XXX>& Get();
// 表格載入:bool Load(const wchar_t* pFilePathName);
// 表格管理器的成員:
// 表格資料列表:m_XXXs,有主鍵的則為map,沒有主鍵的則為vector
main函式涉及兩個小功能:
一個是遍歷輸入引數,解析名名稱空間,待處理的csv檔案,程式碼生成路徑
一個是迭代每一個待處理的csv檔名,傳入CsvParse解析,然後對解析完的結果,逐個生成相應程式碼
完整的程式碼,比較長,這裡就不貼了
給一個生成後的程式碼的示例吧:
還是前文的例子:
如上表格,namespace=common.table.csv
生成的標頭檔案如下所示,
#ifndef __TestCsvLoader_h__
#define __TestCsvLoader_h__
#include <string>
#include <vector>
#include <map>
namespace common{
namespace table{
namespace csv{
class Test
{
public:
class Struct
{
public:
Struct();
Struct(const Struct &other);
Struct& operator = (const Struct &other);
Struct& operator = (const std::string &other);
unsigned int m_StructId;
int m_StructNum;
float m_StructFloat;
std::string m_StruceName;
};
class StructList
{
public:
StructList();
StructList(const StructList &other);
StructList& operator = (const StructList &other);
StructList& operator = (const std::string &other);
unsigned int m_StructId;
int m_StructNum;
float m_StructFloat;
std::string m_StruceName;
};
Test();
Test(const Test &other);
Test& operator = (const Test &other);
unsigned int m_Id1; //測試主鍵1
int m_Id2; //測試主鍵2
unsigned int m_Id3; //測試主鍵3
std::string m_Str; //測試字串
std::vector<unsigned int> m_UInts; //測試數字列表
float m_Float; //測試數字
Struct m_Struct; //測試子結構
std::vector<StructList> m_StructLists; //測試子結構列表
};
class TestCsvLoader
{
public:
TestCsvLoader()
{
}
~TestCsvLoader()
{
}
const Test* Get(unsigned int id1, int id2, unsigned int id3) const;
const std::map<unsigned int, Test>* Get(unsigned int id1, int id2) const;
const std::map<int, std::map<unsigned int, Test> >* Get(unsigned int id1) const;
const std::map<unsigned int, std::map<int, std::map<unsigned int, Test> > >& Get() const;
bool LoadFile(const char* szPath);
bool ReloadFile(const char* szPath);
private:
std::map<unsigned int, std::map<int, std::map<unsigned int, Test> > > m_Tests;
};
}
}
}
#endif
放上完整工程的下載連結
工程中的boost庫的路徑為作者本機的路徑,VS編譯時請選擇Release,重新配置boost庫的include路徑和lib路徑