模板引擎使用-自動生成代碼

1肘迎、 引言

最近用python寫了一個模板引擎安皱,類似Django模板的語法。其實最初寫這東西的動機(jī)是想在C++用模板實現(xiàn)一些機(jī)械的代碼房匆。但是C++模板有些東西實現(xiàn)不了,或太難實現(xiàn)。比較C++模板不是這么容易可以掌握的浴鸿。那難道這些機(jī)械的代碼真的需要每次都手敲嗎井氢?或復(fù)制粘貼再修改?于是我自己給出了另一個方案岳链,既然C++模板這么難花竞,那我自己實現(xiàn)一個自己的模板,語法自己定掸哑,而且不限定只能C++用约急,所有語言都可以使用。項目地址CodeTemplate

不過需要提醒的是C++的模板還是很有必要深入學(xué)習(xí)的苗分,有些東西用模板寫出來有超乎想象的功能厌蔽,包括泛型編程,和模板元編程摔癣。不過在還沒有完全掌握之前用我這套方案還是可以的奴饮,而且模板引擎與目標(biāo)語言無關(guān),這個是本項目最大的優(yōu)勢择浊。

其實模板引擎是一個很早的概念了戴卜,在WEB開發(fā)無處不在,像JAVA的struts2有自己的標(biāo)簽琢岩,JSP也有標(biāo)簽投剥,Python 的Django有自己的模板語法。JS的框架就更多了担孔,像谷歌的Angular江锨,像Facebook的React,Vue我沒有接觸過攒磨,不過我打賭肯定有泳桦。

本項目,需要做詞法分析娩缰,語法分析灸撰,但代碼不超過300行。其中的思想我是參考Haskell的語法分析框架拼坎,以及王垠當(dāng)年C++的Parser浮毯。當(dāng)時看王垠的代碼的確被震撼到了,原來代碼可以這么寫泰鸡,從那時候才理解到函數(shù)式編程的威力债蓝。感覺突然進(jìn)入了一個以前從未想象過的境界。以前覺得面向?qū)ο蟮乃伎寄J綄嵲谑撬蚺沂⒘洌F(xiàn)在覺得代碼無所謂OO饰迹。而且覺得很多情況OO的思考方式是不對的芳誓。以后找機(jī)會寫一遍文章來介紹下王垠的解釋器。

本項目本來想用lua來實現(xiàn)的啊鸭,但是對lua不熟悉锹淌,算了Python吧,而且我看中Python的exec赠制,eval這兩個函數(shù)赂摆,有這兩個函數(shù)的話實在方便很多。

2钟些、 語法

變量

{{%VarName%}}

用{{%%}}括起來的代表變量,模板引擎會根據(jù)傳進(jìn)來的數(shù)據(jù)來替換這個變量

循環(huán)

{% for it in xlist %}
{{%it%}}
{% endfor %}

循環(huán)我只實現(xiàn)了for,不過我覺的應(yīng)該足夠了烟号。需要{%for python語句%}{% endfor %}括起來。模板引擎會將循環(huán)輸出括起來的內(nèi)容政恍。for循環(huán)里面可以嵌套for循環(huán)汪拥。

條件

{% if 條件 %}
內(nèi)容
{% endif %}

模板引擎會根據(jù)條件是否滿足來決定是否顯示里面的內(nèi)容。

3抚垃、以C++處理命令行參數(shù)為例

C++處理命令行參數(shù)很多使用getopt來處理喷楣,但是其實我很不喜歡這樣的處理方式趟大。C++ Main函數(shù)定義如下:

int main(int argc, char* argv[]){}

命令行參數(shù)是通過argc,argv傳進(jìn)來的鹤树。我希望把a(bǔ)rgc,argv轉(zhuǎn)成一個結(jié)構(gòu)體逊朽,這樣比較方便處理罕伯。如一個命令行參數(shù)由

-x -cs -c -b 

這四個參數(shù)構(gòu)成,其中-x -cs 后面是需要帶其他參數(shù)的叽讳,-c追他,-b后面是不帶參數(shù)的,我還要按順序記錄下其他參數(shù)如命令行如下:

Text.exe -x XMLName -cs CSName -c -b  TagFileName  xx1 xx2 xx3 xx4

我希望把這些參數(shù)記錄到一個如下的數(shù)據(jù)結(jié)構(gòu)中

struct ARGS
{
    string FileName;
    string XMLFileName;
    string CSFileName;
    bool CFlag;
    bool BFlag;
    vector<string> Other;

};

其中把 XMLName存到XMLFileName中岛蚤;CSName存到CSFileName中邑狸;有-c參數(shù)的話CFlag就為true呵哨,否則為false圾结;-b同-c;TagFileName存在FileName浇雹,其他參數(shù)存在Other的vector中她紫。我們需要一個函數(shù)來作為轉(zhuǎn)換函數(shù)硅堆,我們暫且叫FromStringList把,函數(shù)類型如下:

ARGS FromStringList(int argc, char *argv[]);

我們用一個數(shù)據(jù)來描述我們的需求,用python的字典來描述

codecontext = {
    'Name':'ARGS',
    'Rules':[
        {'IsPrefix':0,'ArgName':'','Name':"FileName",'Type':'string'},
        {'IsPrefix':1,'ArgName':'-x','Name':"XMLFileName",'Type':'string'},
        {'IsPrefix':1,'ArgName':'-cs','Name':"CSFileName",'Type':'string'},
        {'IsPrefix':0,'ArgName':'-c','Name':"CFlag",'Type':'bool'},
        {'IsPrefix':0,'ArgName':'-b','Name':"BFlag",'Type':'bool'},
    ]
}

ARGS指的是結(jié)構(gòu)體的名字

參數(shù)名 意義
IsPrefix 0代表后面不帶參數(shù),1代表后面需要帶參數(shù)
ArgName 命令行參數(shù)名
Name C++結(jié)構(gòu)體屬性名字
Type C++結(jié)構(gòu)體屬性類型

使用項目的模板語言來實現(xiàn)如下


from CodeTemplate import Template
code = '''
struct {{%Name%}}
{
{% for it in Rules %}
    {{%it['Type']%}} {{%it['Name']%}};
{% endfor %}
    vector<string> Other;

    static ARGS FromStringList(int argc, char *argv[])
    {
        ARGS ret ;
        {% for it in Rules %}
        {% if it['IsPrefix']==0 and it['Type'] == 'bool' %}
        ret.{{%it['Name']%}} = false;
        {% endif %}
        {% endfor %}

        int i=0;
        while(++i<argc)
        {
            string current = argv[i];
            {% for it in Rules %}
            {% if it['IsPrefix']==1 and it['Type'] == 'string' %}
            if(current.compare("{{%it['ArgName']%}}") == 0){
                if(++i>=argc) throw exception("After {{%it['ArgName']%}} Is Nothing");
                ret.{{%it['Name']%}} = argv[i];
                continue;
            }
            {% endif %}
            {% endfor %}

            {% for it in Rules %}
            {% if it['IsPrefix']==0 and it['Type'] == 'bool' %}
            if(current.compare("{{%it['ArgName']%}}") == 0 ){
                ret.{{%it['Name']%}} = true;
                continue;
            }
            {% endif %}
            {% endfor %}

            {% for it in Rules %}
            {% if it['ArgName']=="" %}
            if(ret.{{%it['Name']%}}.empty())
            {
                ret.{{%it['Name']%}} = current;
                continue;
            }
            {% endif %}
            {% endfor %}
            ret.Other.push_back(current);
        }
        return ret;
    }

};
'''
#<RULE IsPrefix="0" ArgName="" Name="FileName" Type="string"/>
codecontext = {
    'Name':'ARGS',
    'Rules':[
        {'IsPrefix':0,'ArgName':'','Name':"FileName",'Type':'string'},
        {'IsPrefix':1,'ArgName':'-x','Name':"XMLFileName",'Type':'string'},
        {'IsPrefix':1,'ArgName':'-cs','Name':"CSFileName",'Type':'string'},
        {'IsPrefix':0,'ArgName':'-c','Name':"CFlag",'Type':'bool'},
        {'IsPrefix':0,'ArgName':'-b','Name':"BFlag",'Type':'bool'},
    ]
}
print(Template(code)(codecontext))

輸出內(nèi)容C++代碼

struct ARGS
{
    string FileName;
    string XMLFileName;
    string CSFileName;
    bool CFlag;
    bool BFlag;
    vector<string> Other;
    static ARGS FromStringList(int argc, char *argv[])
    {
        ARGS ret ;
        ret.CFlag = false;
        ret.BFlag = false;
        int i=0;
        while(++i<argc)
        {
            string current = argv[i];
            if(current.compare("-x") == 0){
                if(++i>=argc) throw exception("After -x Is Nothing");
                ret.XMLFileName = argv[i];
                continue;
            }
            if(current.compare("-cs") == 0){
                if(++i>=argc) throw exception("After -cs Is Nothing");
                ret.CSFileName = argv[i];
                continue;
            }
            if(current.compare("-c") == 0 ){
                ret.CFlag = true;
                continue;
            }
            if(current.compare("-b") == 0 ){
                ret.BFlag = true;
                continue;
            }
            if(ret.FileName.empty())
            {
                ret.FileName = current;
                continue;
            }
            ret.Other.push_back(current);
        }
        return ret;
    }
};

我們把它拷入工程測試一下

#include <iostream>
#include <iostream>
#include <stdexcept>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
struct ARGS
{
    string FileName;
    string XMLFileName;
    string CSFileName;
    bool CFlag;
    bool BFlag;
    vector<string> Other;
    static ARGS FromStringList(int argc, char *argv[])
    {
        ARGS ret;
        ret.CFlag = false;
        ret.BFlag = false;
        int i = 0;
        while (++i<argc)
        {
            string current = argv[i];
            if (current.compare("-x") == 0) {
                if (++i >= argc) throw exception("After -x Is Nothing");
                ret.XMLFileName = argv[i];
                continue;
            }
            if (current.compare("-cs") == 0) {
                if (++i >= argc) throw exception("After -cs Is Nothing");
                ret.CSFileName = argv[i];
                continue;
            }
            if (current.compare("-c") == 0) {
                ret.CFlag = true;
                continue;
            }
            if (current.compare("-b") == 0) {
                ret.BFlag = true;
                continue;
            }
            if (ret.FileName.empty())
            {
                ret.FileName = current;
                continue;
            }
            ret.Other.push_back(current);
        }
        return ret;
    }
};
int main(int argc, char* argv[])
{
    try
    {
        auto _ARGS = ARGS::FromStringList(argc, argv);
        cout << "CSFileName:" << _ARGS.CSFileName << endl;
        cout << "XMLFileName:" << _ARGS.XMLFileName << endl;
        cout << "FileName:" << _ARGS.FileName << endl;
        cout << "BFlag:" << _ARGS.BFlag << endl;
        cout << "CFlag:" << _ARGS.CFlag << endl;

        for (auto it = _ARGS.Other.begin(); it != _ARGS.Other.end(); it++)
        {
            cout << "Other:" << *it << endl;
        }
    }
    catch (exception& ex)
    {
        cout << ex.what() << endl;
    }
}  

測試結(jié)果

D:\VSWorkSpace\CTEST\Debug>CTEST.exe -cs csName -x xmlname -b -c tagfilename x1 x2 x3
CSFileName:csName
XMLFileName:xmlname
FileName:tagfilename
BFlag:1
CFlag:1
Other:x1
Other:x2
Other:x3

D:\VSWorkSpace\CTEST\Debug>CTEST.exe -cs
After -cs Is Nothing

D:\VSWorkSpace\CTEST\Debug>CTEST.exe -x
After -x Is Nothing

D:\VSWorkSpace\CTEST\Debug>CTEST.exe tag x1 x3 x3
CSFileName:
XMLFileName:
FileName:tag
BFlag:0
CFlag:0
Other:x1
Other:x3
Other:x3

其實我是為了測試for贿讹,if渐逃,變量才這么寫。如果用python的高階函數(shù)實現(xiàn)的話代碼會簡潔一些民褂。如下

from CodeTemplate import Template
code = '''
struct {{%Name%}}
{
    {% for it in Rules %}
    {{%it['Type']%}} {{%it['Name']%}};
    {% endfor %}
    vector<string> Other;

    static ARGS FromStringList(int argc, char *argv[])
    {
        ARGS ret ;
        {% for it in filter(lambda it:it['IsPrefix']==0 and it['Type'] == 'bool',Rules) %}
        ret.{{%it['Name']%}} = false;
        {% endfor %}

        int i=0;
        while(++i<argc)
        {
            string current = argv[i];
            {% for it in filter(lambda it:it['IsPrefix']==1 and it['Type'] == 'string',Rules) %}
            if(current.compare("{{%it['ArgName']%}}") == 0){
                if(++i>=argc) throw exception("After {{%it['ArgName']%}} Is Nothing");
                ret.{{%it['Name']%}} = argv[i];
                continue;
            }
            {% endfor %}

            {% for it in filter(lambda it: it['IsPrefix']==0 and it['Type'] == 'bool',Rules) %}
            if(current.compare("{{%it['ArgName']%}}") == 0 ){
                ret.{{%it['Name']%}} = true;
                continue;
            }
            {% endfor %}

            {% for it in filter(lambda it:it['ArgName']=="",Rules) %}
            if(ret.{{%it['Name']%}}.empty())
            {
                ret.{{%it['Name']%}} = current;
                continue;
            }
            {% endfor %}
            ret.Other.push_back(current);
        }
        return ret;
    }
};
'''
#<RULE IsPrefix="0" ArgName="" Name="FileName" Type="string"/>
codecontext = {
    'Name':'ARGS',
    'Rules':[
        {'IsPrefix':0,'ArgName':'','Name':"FileName",'Type':'string'},
        {'IsPrefix':1,'ArgName':'-x','Name':"XMLFileName",'Type':'string'},
        {'IsPrefix':1,'ArgName':'-cs','Name':"CSFileName",'Type':'string'},
        {'IsPrefix':0,'ArgName':'-c','Name':"CFlag",'Type':'bool'},
        {'IsPrefix':0,'ArgName':'-b','Name':"BFlag",'Type':'bool'},
    ]
}
print(Template(code)(codecontext))

當(dāng)然它輸出的C++代碼是一樣的茄菊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疯潭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子面殖,更是在濱河造成了極大的恐慌袁勺,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畜普,死亡現(xiàn)場離奇詭異期丰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)吃挑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門钝荡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舶衬,你說我怎么就攤上這事埠通。” “怎么了逛犹?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵端辱,是天一觀的道長。 經(jīng)常有香客問我虽画,道長舞蔽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任码撰,我火速辦了婚禮渗柿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘脖岛。我一直安慰自己朵栖,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布柴梆。 她就那樣靜靜地躺著陨溅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绍在。 梳的紋絲不亂的頭發(fā)上门扇,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音揣苏,去河邊找鬼悯嗓。 笑死,一個胖子當(dāng)著我的面吹牛卸察,可吹牛的內(nèi)容都是我干的脯厨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼坑质,長吁一口氣:“原來是場噩夢啊……” “哼合武!你這毒婦竟也來了临梗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤稼跳,失蹤者是張志新(化名)和其女友劉穎盟庞,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汤善,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡什猖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了红淡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片不狮。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖在旱,靈堂內(nèi)的尸體忽然破棺而出摇零,到底是詐尸還是另有隱情,我是刑警寧澤桶蝎,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布驻仅,位于F島的核電站,受9級特大地震影響登渣,放射性物質(zhì)發(fā)生泄漏噪服。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一绍豁、第九天 我趴在偏房一處隱蔽的房頂上張望芯咧。 院中可真熱鬧,春花似錦竹揍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至带到,卻和暖如春昧碉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背揽惹。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工被饿, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搪搏。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓狭握,卻偏偏與公主長得像,于是被迫代替她去往敵國和親疯溺。 傳聞我的和親對象是個殘疾皇子论颅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容