CodeDump使用通过数据结构描述文件(meta文件)和目标代码模板文件(template文件)生成目标代码的方案。 基本思路是先解析meta文件得到数据结构信息,然后解析template文件得到目标代码的规则信息,最后对template文件展开,填充meta信息,就得到了目标代码。
遍历目录生成代码:
usage1: CodeDump --xmldir=test/xml/ --lang=.h;.cpp;.cs --tpldir=template/ --tardir=test/dumpcode/
单个文件生成代码:
usage2: CodeDump --idl=xxx.idl --lang=cpp;cs --tpldir=template/ --tardir=test/dumpcode/
参数说明:
xmldir idl文件存放目录
lang 生成代码的语言,目前只有.cs、.cpp、.h三种
tpldir 模板文件目录
tardir 生成代码文件目录
必需的配置文件是template/目标代码模板、xml文件的idl描述文件。 注意生成代码的时候要先check out可能被Overwrite的代码文件。
以xml解析生成C++代码为例介绍使用规则。这里使用anniversary_campaign.xml。
<?xml version="1.0" encoding="utf-8"?>
<Config>
<!-- 活动id 嘉年华加成活动id -->
<Misc campaign_id="24001" carnival_activity="24001" />
<Seat>
<!--杂项 ABC级座椅ID-->
<Misc seat_a_id="10400009" seat_b_id="10400010" seat_c_id="10400011" />
<Upgrades>
<!--座椅升级配置,座椅id,升级材料,下级座椅id-->
<Upgrade seat_id="10400009" next_seat_id="10400010">
<Need type="item" boy_id="920000160" girl_id="920000160" num="300" />
</Upgrade>
<Upgrade seat_id="10400010" next_seat_id="10400011">
<Need type="item" boy_id="920000160" girl_id="920000160" num="600" />
</Upgrade>
</Upgrades>
</Seat>
<Signin>
<!--签到送布衣:累计天数-->
<Day dayid="1">
<Reward type="gb" boy_id="0" girl_id="0" num="100" />
</Day>
<Day dayid="2">
<Reward type="gb" boy_id="0" girl_id="0" num="200" />
</Day>
<Day dayid="3">
<Reward type="gb" boy_id="0" girl_id="0" num="300" />
</Day>
<Day dayid="4">
<Reward type="gb" boy_id="0" girl_id="0" num="400" />
</Day>
<Day dayid="5">
<Reward type="gb" boy_id="0" girl_id="0" num="500" />
</Day>
<Day dayid="6">
<Reward type="gb" boy_id="0" girl_id="0" num="600" />
</Day>
<Day dayid="7">
<Reward type="gb" boy_id="0" girl_id="0" num="700" />
</Day>
<Day dayid="8">
<Reward type="gb" boy_id="0" girl_id="0" num="800" />
</Day>
<Day dayid="9">
<Reward type="gb" boy_id="0" girl_id="0" num="900" />
</Day>
<Day dayid="10">
<Reward type="gb" boy_id="0" girl_id="0" num="1000" />
</Day>
</Sigin>
<Passport>
<Share>
<Reward type="gb" boy_id="0" girl_id="0" num="1000" />
</Share>
<Triggers>
<Trigger id="2300031" />
<Trigger id="2300032" />
<Trigger id="2300033" />
<Trigger id="2300034" />
<Trigger id="2300035" />
</Triggers>
</Passport>
</Config>
anniversary_campaign.xml.idl
class RewardInfo{
int type;
int boy_id;
int girl_id;
int num;
}
class SeatNeed{
[reward]
RewardInfo need;
}
class AnnyMisc{
int campaign_id;
int carnival_activity;
}
class SeatMisc{
int seat_a_id;
int seat_b_id;
int seat_c_id;
}
class SeatUpgrade{
int seat_id;
int next_seat_id;
[Need]
SeatNeed need;
}
class Seat{
[tag(Misc)]
SeatMisc misc;
[tag(Upgrades)]
List<SeatUpgrade> upgrade;
}
class Day{
[key]
int dayid;
[reward]
RewardInfo reward;
}
[root]
class AnniversaryConfig
{
[tag(Misc)]
AnnyMisc misc;
Seat seat;
[tag(Signin)]
Dict<int,Day> signin_cfg;
}
说明:
- 采用类似C#语法的规则,Dictionary比较长,简写成了Dict,目前支持类(class)、枚举(enum)、基本数据类型、容器(只支持List和Dict)、特性(attribute)、注释、默认值、引用(using)。
- 特性是为了辅助生成代码使用的,比如[key]标记的字段是类的key字段,用于解析Dict的时候找到key的名字。
- [string]表示这个字段从xml节点的一个属性字符串解析,而不是从一个xml节点解析。
- [reward]表示这个字段是用reward专用读取函数读取的。
- [root]表示这个类是配置文件的根类型,在生成C++代码时需要知道根类型。
- Using引用的meta文件会在C++中解析为#include 头文件。
template_parser.h
//this file is generated by codedump tool at ${G.DATETIME} do NOT edit it !
#ifndef __${Meta.Name}__PARSER_H__
#define __${Meta.Name}__PARSER_H__
#include <map>
#include <vector>
#include <string>
#include <platform/platform_shared/dynamic_config.h>
@{FOREACH(Enum IN ${Meta.EnumList})}
${Enum.Comment}
enum ${Enum.Name}
{
@{FOREACH(Field IN ${Enum.FieldList})}
${Field.Name} = ${Field.Value}, ${Field.Comment}
@{END_FOREACH}
};
@{END_FOREACH}
@{FOREACH(Class IN ${Meta.ClassList})}
${Class.Comment}
struct ${Class.Name}
{
@{FOREACH(Field IN ${Class.FieldList})}
${Field.Type} ${Field.Name}; ${Field.Comment}
@{END_FOREACH}
};
@{END_FOREACH}
class ${Meta.Name}Parser: public CDynamicConfig
{
public:
bool Init(const char* xmlfile);
virtual bool LoadConfig(variant* root);
const ${Meta.RootClassName}& GetConfig(){return m_config;}
private:
@{FOREACH(Class IN ${Meta.ClassList})}
bool Parse${Class.Name}(variant* root, ${Class.Name}& info);
@{END_FOREACH}
private:
${Meta.RootClassName} m_config;
};
#endif //__${Meta.Name}__PARSER_H__
说明: 这是生成C++代码的模板文件,其基本规则如下:
@{\w+}
为“规则标记”。目前有IF/FOREACH/SWITCH三种控制语句和METATEXT文本,足够使用了。${\w+}
为“数据标记”。数据标记通过反射获取注册对象的值,可以灵活定义。
本例中生成的C++代码为anniversary_campaign_parser.h
//this file is generated by codedump tool at 1/14/2019 20:16:15 do NOT edit it !
#ifndef __AnniversaryCampaign__PARSER_H__
#define __AnniversaryCampaign__PARSER_H__
#include <map>
#include <vector>
#include <string>
#include <platform/platform_shared/dynamic_config.h>
struct RewardInfo
{
int type;
int boy_id;
int girl_id;
int num;
};
struct SeatNeed
{
RewardInfo need;
};
struct AnnyMisc
{
int campaign_id;
int carnival_activity;
};
struct SeatMisc
{
int seat_a_id;
int seat_b_id;
int seat_c_id;
};
struct SeatUpgrade
{
int seat_id;
int next_seat_id;
SeatNeed need;
};
struct Seat
{
SeatMisc misc;
std::vector<SeatUpgrade> upgrade;
};
struct Day
{
int dayid;
RewardInfo reward;
};
struct AnniversaryConfig
{
AnnyMisc misc;
Seat seat;
std::map<int,Day> signin_cfg;
};
class AnniversaryCampaignParser: public CDynamicConfig
{
public:
bool Init(const char* xmlfile);
virtual bool LoadConfig(variant* root);
const AnniversaryConfig& GetConfig(){return m_config;}
bool ParseRewardInfo(variant* root, RewardInfo& info);
bool ParseSeatNeed(variant* root, SeatNeed& info);
bool ParseAnnyMisc(variant* root, AnnyMisc& info);
bool ParseSeatMisc(variant* root, SeatMisc& info);
bool ParseSeatUpgrade(variant* root, SeatUpgrade& info);
bool ParseSeat(variant* root, Seat& info);
bool ParseDay(variant* root, Day& info);
bool ParseAnniversaryConfig(variant* root, AnniversaryConfig& info);
private:
AnniversaryConfig m_config;
};
#endif //__AnniversaryCampaign__PARSER_H__
- 文件名中的template会转换为meta名
- 目前支持类(class)、枚举(enum)、基本数据类型(int,bool,string,float)、容器(只支持List和Dict)、特性(attribute)、注释、默认值、引用(using)。
- 需要指定xml的root节点对应的类为[root]。
- 需要指定
dict<k,v>
的v类中作为key的字段为[key]。 - 不存在
Dict<int,int>
这种配置。存在List<int>
配置。 - [string]表示这个字段(class/list)从”1,2,3;4,5,6”这种字符串解析。不指定的话默认从下级节点解析。字符串分隔符是comma(,)和simicolon(;)
- [string]还可以修饰类,默认类从xml节点解析,添加此特性则允许其从string解析。需要配合以[string]修饰的字段使用。
- 类型有依赖关系,所以被依赖的class要写在前面,如果引用了其他idl文件里的类型需要在文件开始的地方引用一下比如:using common_meta。
- 尽量不要混搭风格!节点尽量写完整!
- 每个具名节点,在IDL中填写为class、List、Dict。Class的成员对应xml中的属性。举例:
- Class、dict、list成员如果为普通类型,也可以用inner_text的配置方式。 例子一:
例子二:
- 可以省略dict和list的中间节点。 标准写法:
省略写法:
对应idl类:
- 不支持的配置方式: 例子一:既有attribute又有inner text,说明tip节点是个类,但丢失了一种字段描述。
例子二:不确定的配置格式,根据需求扩展节点。这种情况只能拆表,根据道具类型拆分。否则就要在idl中定义所有类型,结果会非常冗余。
例子三:不支持复杂嵌套类从string解析。混搭格式也不支持。