1. 程式人生 > >程式碼自動生成工具(一)-Csv讀表程式碼自動生成工具

程式碼自動生成工具(一)-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路徑