初级代码游戏的专栏介绍与文章目录-CSDN博客
我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。
这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。
专题:一个自制代码生成器(嵌入式脚本语言)之总述-CSDN博客
专题:一个自制代码生成器(嵌入式脚本语言)之对象模型-CSDN博客
专题:一个自制代码生成器(嵌入式脚本语言)之堆栈结构和总入口-CSDN博客
专题:一个自制代码生成器(嵌入式脚本语言)之核心逻辑-CSDN博客
专题:一个自制代码生成器(嵌入式脚本语言)之辅助逻辑-CSDN博客
专题:一个自制代码生成器(嵌入式脚本语言)之应用实例-CSDN博客
专题:一个自制代码生成器(嵌入式脚本语言)之模型开发-CSDN博客(本篇)
专题:一个自制代码生成器(嵌入式脚本语言)之代码模板详解-CSDN博客
这是对数据库操作的模型化实现,原则上与代码生成器无关,对同样的模型也可以直接写函数操作数据库,就像普通的动态方法一样。不过使用了代码生成器生成静态代码性能会比较高,也比较方便检查错误。
目录
一、回顾一下对象模型CCTObject
二、各种要素的定义
2.1 列 字段 member
2.2 索引 index
2.3 列组 group
2.4 增删改查 DML
2.5 表 table
2.6 序列 sequence
三、骨干代码
四、使用模型
五、代码模板详解
一、回顾一下对象模型CCTObject
代码生成器接受的模型用CCTObject来定义,和一般脚本语言定义对象差不多,通过级联实现任意复杂的对象树,主要结构如下:
struct CCTObject
{enum TYPE { OBJECT, POINTER, PROPERTY, ARRAY };TYPE m_type;//根据type决定哪个成员有效CCTObject* m_Pointer;//指针string m_Property;//属性(值)map<string, CCTObject > m_Object;//对象vector<CCTObject > m_Array;//数组
......
}
整个模型最终生成到一个CCTObject对象里面。
二、各种要素的定义
2.1 列 字段 member
对应数据库的列,最关键的属性是名字和类型。
//成员变量定义struct member{//group为空则为列定义,使用下面一组成员string member_name;string member_comment;//单行string member_type;//long time double stringstring member_default;//默认值string member_show_type;//显示方式//group不为空则为列组访问,使用下面一组成员string group;//组名,string var_name;//变量名//生成数据库类型string makeDBType()const{stringstream ss;if ("string" == member_type)ss << "COLUMN_TYPE_STRING_POOL";else if ("long" == member_type)ss << "COLUMN_TYPE_LONG";else if ("time" == member_type)ss << "COLUMN_TYPE_LONG";else if ("double" == member_type)ss << "COLUMN_TYPE_DOUBLE";else ss << "错误的类型 " << member_type;return ss.str();}//生成参数类型string makeParamType()const{stringstream ss;if ("string" == member_type)ss << "char const *";else if ("long" == member_type)ss << "long const";else if ("time" == member_type)ss << "long const";else if ("double" == member_type)ss << "double const";else ss << "错误的类型 " << member_type;return ss.str();}//生成参数值string makeParamValue()const{stringstream ss;if ("string" == member_type)ss << member_name << ".c_str()";else if ("long" == member_type)ss << member_name;else if ("time" == member_type)ss << member_name;else if ("double" == member_type)ss << member_name;else ss << "错误的类型 " << member_type;return ss.str();}//生成printf类型string makePrintfType()const{stringstream ss;if ("string" == member_type)ss << "%s";else if ("long" == member_type)ss << "%ld";else if ("time" == member_type)ss << "%ld";//else if ("double" == member_type)ss << "%f";else ss << "错误的类型 " << member_type;return ss.str();}//生成HTML输出类型string makeHtmlType()const{stringstream ss;if ("string" == member_type)ss << "CHtmlDoc::CHtmlDoc_DATACLASS_LEFT";else if ("time" == member_type)ss << "CHtmlDoc::CHtmlDoc_DATACLASS_TIME";else ss << "CHtmlDoc::CHtmlDoc_DATACLASS_RIGHT";return ss.str();}//输出到CCTObjectCCTObject toCCTObject(){CCTObject O;if (0 == group.size()){O.SetObjectAddProperty("name", member_name);O.SetObjectAddProperty("GetName", member_name);O.SetObjectAddProperty("GroupIndexVar", "");O.SetObjectAddProperty("comment", member_comment);O.SetObjectAddProperty("type", ("time" == member_type ? "long" : member_type));O.SetObjectAddProperty("default", member_default);O.SetObjectAddProperty("show_type", member_show_type);O.SetObjectAddProperty("DBType", makeDBType());O.SetObjectAddProperty("ParamType", makeParamType());O.SetObjectAddProperty("ParamName", member_name);O.SetObjectAddProperty("PrintfType", makePrintfType());O.SetObjectAddProperty("ParamValue", makeParamValue());O.SetObjectAddProperty("HtmlType", makeHtmlType());}else{O.SetObjectAddProperty("name", var_name);O.SetObjectAddProperty("GetName", "\" + GetGroup" + group + "ColName(" + var_name + ") + \"");O.SetObjectAddProperty("GroupIndexVar", " " + var_name + ",");O.SetObjectAddProperty("ParamType", "long " + var_name + ", long");O.SetObjectAddProperty("ParamName", var_name);O.SetObjectAddProperty("PrintfType", "%ld");}return O;}};
成员变量的功能是比较显而易见的,另外包含了一组把成员变量的值变成对象模型的属性的方法,最后由toCCTObject()方法变成数据模型对象。
2.2 索引 index
索引比较简单,因为只是在创建表的时候用一下:
//索引定义struct index{string index_name;string index_comment;bool unique{ false };string index_members;//输出到CCTObjectCCTObject toCCTObject(){CCTObject O;O.SetObjectAddProperty("name", index_name);O.SetObjectAddProperty("comment", index_comment);O.SetObjectAddProperty("unique", (unique ? "true" : "false"));O.SetObjectAddProperty("members", index_members);return O;}};
2.3 列组 group
这个只是为了简化经常使用的一组列,并不对应数据库概念:
//列组定义(可以用下标标识的多个列)struct group{string group_name;string group_comment;vector<member > group_members;//输出到CCTObjectCCTObject toCCTObject(){CCTObject O;O.SetObjectAddProperty("name", group_name);O.SetObjectAddProperty("comment", group_comment);for (vector<member >::iterator it = group_members.begin(); it != group_members.end(); ++it){O.SetObjectArrayPushBack("members", it->toCCTObject());}return O;}};
2.4 增删改查 DML
所有SQL都用函数包装起来,这样客户代码不会直接使用SQL,等于所有对数据库的操作都集中在一起,以后想找出来哪些表被用到、哪些没有用到就很容易了,只要包装函数没有被调用自然就没有被用到。
//DML定义struct dml{string dml_name;string dml_type;//select insert update deletestring dml_comment;vector<member > op_members;//操作的列bool hasWhere;//是否有where,当where_members和other_where均为空则不添加此属性vector<member > where_members;//条件列,仅支持 and =string other_where;//直接写死的条件部分//输出到CCTObjectCCTObject toCCTObject(){hasWhere = (where_members.size() != 0 || other_where.size() != 0);CCTObject O;O.SetObjectAddProperty("name", dml_name);O.SetObjectAddProperty("type", dml_type);O.SetObjectAddProperty("comment", dml_comment);for (vector<member >::iterator it = op_members.begin(); it != op_members.end(); ++it){O.SetObjectArrayPushBack("op_members", it->toCCTObject());}if (hasWhere)O.SetObjectAddProperty("hasWhere", hasWhere);for (vector<member >::iterator it = where_members.begin(); it != where_members.end(); ++it){O.SetObjectArrayPushBack("where_members", it->toCCTObject());}if(other_where.size() != 0)O.SetObjectAddProperty("other_where", other_where);return O;}};
这个模型不允许直接传入SQL的片段,因为这样会破坏封装,实际数据库操作无法预期。
2.5 表 table
表对象包含以上所有部分,对应一张实体表。
class table{public:string table_name;string table_comment;vector<member > table_members;string PK_cols;//主键的列vector<index > table_indexs;//主键名称为PKvector<group > table_groups;//可以用下标访问的列组vector<dml > table_dmls;//数据操作,对应一个sqlmember * _getMember(string const & name){for (size_t i = 0; i < table_members.size(); ++i){if (name == table_members[i].member_name)return &table_members[i];}return NULL;}bool SetTable(char const * name, char const * comment){table_name = name;table_comment = comment;return true;}bool AddMember(char const * name, char const * type, char const * comment, char const * _default = "", char const * _show_type=""){member m;m.member_name = name;m.member_type = type;m.member_comment = comment;m.member_default = _default;m.member_show_type = _show_type;if ("string" == m.member_type)m.member_default = "\"" + m.member_default + "\"";else{if (0 == m.member_default.size())m.member_default = "0";}table_members.push_back(m);return true;}//设置主键bool SetPK(char const * PK){PK_cols = PK;StringTokenizer st(PK_cols, ",");for (size_t i = 0; i < st.size(); ++i){member * p = _getMember(st[i].c_str());if (NULL == p){thelog << "主键指定的列不存在 " << st[i] << ende;exit(0);}}return true;}bool AddIndex(char const * name, bool unique,char const * members, char const * comment){StringTokenizer st(members, ",");index tmp;tmp.index_name = name;tmp.unique = unique;tmp.index_comment = comment;tmp.index_members = members;//检查for (size_t i = 0; i < st.size(); ++i){member* p = _getMember(st[i].c_str());if (NULL == p){thelog << "索引指定的列不存在 " << st[i] << ende;exit(0);}}table_indexs.push_back(tmp);return true;}bool AddGroup(char const * name, char const * members, char const * comment){StringTokenizer st(members, ",");group tmp;tmp.group_name = name;tmp.group_comment = comment;for (size_t i = 0; i < st.size(); ++i){member * p = _getMember(st[i].c_str());if (NULL == p){thelog << "列组指定的列不存在 " << st[i] << ende;exit(0);}tmp.group_members.push_back(*p);}table_groups.push_back(tmp);return true;}bool AddDML(char const * name, char const * type, char const * op_members, char const * where_members, char const * comment, char const * other_where = ""){dml tmp;tmp.dml_name = name;tmp.dml_type = type;tmp.dml_comment = comment;tmp.other_where = other_where;if (0 != strlen(op_members)){StringTokenizer st(op_members, ",");for (size_t i = 0; i < st.size(); ++i){if ("*" == st[i]){//全部for (vector<member >::const_iterator it = table_members.begin(); it != table_members.end(); ++it){tmp.op_members.push_back(*it);}}else{StringTokenizer st2(st[i], ":");if (2 == st2.size()){//列组bool isGroupFound = false;for (vector<group >::const_iterator it = table_groups.begin(); it != table_groups.end(); ++it){if (it->group_name == st2[0]){isGroupFound = true;}}if (!isGroupFound){thelog << "op_members指定的列组不存在 " << st2[0] << ende;exit(0);}member tmpmember;tmpmember.group = st2[0];tmpmember.var_name = st2[1];tmp.op_members.push_back(tmpmember);}else{//简单列member * p = _getMember(st[i].c_str());if (NULL == p){thelog << "op_members指定的列不存在 " << st[i] << ende;exit(0);}tmp.op_members.push_back(*p);}}}}if (0 != strlen(where_members)){StringTokenizer st(where_members, ",");for (size_t i = 0; i < st.size(); ++i){member * p = _getMember(st[i].c_str());if (NULL == p){thelog << "where_members指定的列不存在 " << st[i] << ende;exit(0);}tmp.where_members.push_back(*p);}}table_dmls.push_back(tmp);return true;}//输出到CCTObjectCCTObject toCCTObject(){CCTObject O;O.SetObjectAddProperty("name", table_name);O.SetObjectAddProperty("comment", table_comment);O.SetObjectAddProperty("PK_cols", PK_cols);for (vector<member >::iterator it = table_members.begin(); it != table_members.end(); ++it){O.SetObjectArrayPushBack("members", it->toCCTObject());}for (vector<index >::iterator it = table_indexs.begin(); it != table_indexs.end(); ++it){O.SetObjectArrayPushBack("indexs", it->toCCTObject());}for (vector<group >::iterator it = table_groups.begin(); it != table_groups.end(); ++it){O.SetObjectArrayPushBack("groups", it->toCCTObject());}for (vector<dml >::iterator it = table_dmls.begin(); it != table_dmls.end(); ++it){O.SetObjectArrayPushBack("dmls", it->toCCTObject());}return O;}};
2.6 序列 sequence
序列是个比较头疼的东西,因为不同数据库的支持很不一样,Oracle的序列是独立的数据库对象,Sql Server则是列可以被指定为自增列。
class sequence{public:string sequence_name;string sequence_init;string sequence_comment;//输出到CCTObjectCCTObject toCCTObject(){CCTObject O;O.SetObjectAddProperty("name", sequence_name);O.SetObjectAddProperty("comment", sequence_comment);O.SetObjectAddProperty("init", sequence_init);return O;}};
三、骨干代码
class CCTModel_UniversalDB
{
private:vector<table > m_tables;vector<sequence > m_sequences;set<string > _table_names;//用来检查是否已经存在set<string > _sequence_names;//用来检查是否已经存在public:CCTObject toCCTObject(){CCTObject O;O.SetObjectAddProperty("name", "CodeTemplate");for (vector<table >::iterator it = m_tables.begin(); it != m_tables.end(); ++it){O.SetObjectArrayPushBack("tables", it->toCCTObject());}for (vector<sequence >::iterator it = m_sequences.begin(); it != m_sequences.end(); ++it){O.SetObjectArrayPushBack("sequences", it->toCCTObject());}return O;}sequence * AddNewSequence(char const * sequencename, char const * comment, long init){if (_sequence_names.end() != _sequence_names.find(sequencename)){thelog << sequencename << " 已经存在" << ende;return NULL;}else{_sequence_names.insert(sequencename);}m_sequences.resize(m_sequences.size() + 1);sequence * p = &m_sequences[m_sequences.size() - 1];p->sequence_name = sequencename;p->sequence_comment = comment;char buf[256];sprintf(buf, "%ld", init);p->sequence_init = buf;return p;}table * AddNewTable(char const * tablename, char const * comment){if (_table_names.end() != _table_names.find(tablename)){thelog << tablename << " 已经存在" << ende;return NULL;}else{_table_names.insert(tablename);}m_tables.resize(m_tables.size() + 1);table * p = &m_tables[m_tables.size() - 1];p->SetTable(tablename, comment);return p;}
public://_ns 名字空间,sys 总类的名称的一部分bool CreateCode(char const * _ns, char const * sys, char const * output_dir){CCTObject O;O = toCCTObject();O.SetObjectAddProperty("NAMESPACE", _ns);O.SetObjectAddProperty("SYS", sys);CCTStack S;CCodeTemplate ct;string str;//thelog << endl << O.toString(str) << endi;S.clear();if (!ct.ProcessFile("_t_UniversalDB.h.ct", (string(output_dir) + "/_cc_" + sys + ".h").c_str(), O, S))return false;vector<CCTObject > * p;if (NULL != O.FindObject("tables")){p = &O.FindObject("tables")->m_Array;for (vector<CCTObject >::iterator it = p->begin(); it != p->end(); ++it){S.clear();S.Push();S.Add("table", *it);//thelog << endl << S.toString(str) << endi;if (!ct.ProcessFile("_t_UniversalDB_table.h.ct", (string(output_dir) + "/_cc_" + sys + "_" + it->GetDefaultValue() + ".h").c_str(), O, S))return false;}}if (NULL != O.FindObject("sequences")){p = &O.FindObject("sequences")->m_Array;for (vector<CCTObject >::iterator it = p->begin(); it != p->end(); ++it){S.clear();S.Push();S.Add("sequence", *it);thelog << endl << S.toString(str) << endi;if (!ct.ProcessFile("_t_UniversalDB_sequence.h.ct", (string(output_dir) + "/_cc_" + sys + "_" + it->GetDefaultValue() + ".h").c_str(), O, S))return false;}}return true;}};
代码很容易懂。
四、使用模型
专题:一个自制代码生成器(嵌入式脚本语言)之应用实例-CSDN博客
五、代码模板详解
下一篇:专题:一个自制代码生成器(嵌入式脚本语言)之代码模板详解-CSDN博客
(这里是结束但不是整个系列的结束)