专题:一个自制代码生成器(嵌入式脚本语言)之模型开发

初级代码游戏的专栏介绍与文章目录-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 string
		string 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();
		}

		//输出到CCTObject
		CCTObject 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;

		//输出到CCTObject
		CCTObject 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;

		//输出到CCTObject
		CCTObject 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 delete
		string dml_comment;
		vector<member > op_members;//操作的列
		bool hasWhere;//是否有where,当where_members和other_where均为空则不添加此属性
		vector<member > where_members;//条件列,仅支持 and =
		string other_where;//直接写死的条件部分

		//输出到CCTObject
		CCTObject 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;//主键名称为PK
		vector<group > table_groups;//可以用下标访问的列组
		vector<dml > table_dmls;//数据操作,对应一个sql

		member * _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;
		}
		//输出到CCTObject
		CCTObject 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;

		//输出到CCTObject
		CCTObject 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博客


(这里是结束但不是整个系列的结束)

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值