Skip to content

jwk000/CodeDump

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CodeDump 模板代码生成方案

概述

CodeDump使用通过数据结构描述文件(meta文件)和目标代码模板文件(template文件)生成目标代码的方案。 基本思路是先解析meta文件得到数据结构信息,然后解析template文件得到目标代码的规则信息,最后对template文件展开,填充meta信息,就得到了目标代码。

CodeDump工具使用说明

遍历目录生成代码:

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解析代码

以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>

1. 填写xml对应的IDL文件

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 头文件。

2. 填写目标代码template文件

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+} 为“数据标记”。数据标记通过反射获取注册对象的值,可以灵活定义。

3. 使用CodeDump工具生成代码

本例中生成的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__

IDL填写规范

  1. 文件名中的template会转换为meta名
  2. 目前支持类(class)、枚举(enum)、基本数据类型(int,bool,string,float)、容器(只支持List和Dict)、特性(attribute)、注释、默认值、引用(using)。
  3. 需要指定xml的root节点对应的类为[root]。
  4. 需要指定dict<k,v>的v类中作为key的字段为[key]。
  5. 不存在Dict<int,int>这种配置。存在List<int>配置。
  6. [string]表示这个字段(class/list)从”1,2,3;4,5,6”这种字符串解析。不指定的话默认从下级节点解析。字符串分隔符是comma(,)和simicolon(;)
  7. [string]还可以修饰类,默认类从xml节点解析,添加此特性则允许其从string解析。需要配合以[string]修饰的字段使用。
  8. 类型有依赖关系,所以被依赖的class要写在前面,如果引用了其他idl文件里的类型需要在文件开始的地方引用一下比如:using common_meta。

Xml填写规范

  1. 尽量不要混搭风格!节点尽量写完整!
  2. 每个具名节点,在IDL中填写为class、List、Dict。Class的成员对应xml中的属性。举例:

  1. Class、dict、list成员如果为普通类型,也可以用inner_text的配置方式。 例子一:

例子二:

  1. 可以省略dict和list的中间节点。 标准写法:

省略写法:

对应idl类:

  1. 不支持的配置方式: 例子一:既有attribute又有inner text,说明tip节点是个类,但丢失了一种字段描述。

例子二:不确定的配置格式,根据需求扩展节点。这种情况只能拆表,根据道具类型拆分。否则就要在idl中定义所有类型,结果会非常冗余。

例子三:不支持复杂嵌套类从string解析。混搭格式也不支持。

About

一种通用的代码生成方案

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages