技术宝典 | NeCodeGen:基于 clang 的源到源转译工具
導(dǎo)讀:我們生活在一個(gè)多樣的世界:豐富多樣的操作系統(tǒng)、豐富多樣的編程語言、豐富多樣的技術(shù)棧,如此豐富多樣的技術(shù)棧為軟件提供商帶來了的挑戰(zhàn):如何快速覆蓋這些系統(tǒng)/技術(shù)棧以滿足不同背景的用戶的需求?本文基于網(wǎng)易云信的落地場景,詳細(xì)介紹了基于 clang 的源到源轉(zhuǎn)譯工具。
文|開開
網(wǎng)易云信資深 C++ 開發(fā)工程師
01 前言
我們生活在一個(gè)多樣的世界: ?豐富多樣的操作系統(tǒng)、豐富多樣的編程語言、豐富多樣的技術(shù)棧,下面是對前端一個(gè)粗略地統(tǒng)計(jì):
?
如此豐富多樣的技術(shù)棧為軟件提供商帶來了的挑戰(zhàn):如何快速覆蓋這些系統(tǒng)/技術(shù)棧以滿足不同背景的用戶的需求?
以網(wǎng)易云信 IM 為例,它的研發(fā)流程大致如下:
?
隨著業(yè)務(wù)發(fā)展,網(wǎng)易云信 IM 的 API 越來越多(有幾百個(gè)),為適配其他平臺,工程師需要投入大量的時(shí)間來編寫 language binding,這部分工作冗雜、耗時(shí)、且重復(fù)性極大;在維護(hù)階段,對 C++ 接口的修改都需要同步到各個(gè) language binding 中,稍有遺漏則會導(dǎo)致問題。為提高生產(chǎn)力和研發(fā)效率,將工程師從重復(fù)且繁重的"體力活"中解放出來讓其更專注于重要功能的研發(fā),?網(wǎng)易云信的大前端團(tuán)隊(duì)研發(fā)了基于 clang 的源到源轉(zhuǎn)譯工具 NeCodeGen,本文將對 NeCodeGen 進(jìn)行介紹,以期為面臨相同問題的工程師們提供解決問題的方法與思路。
02 為什么要重造輪子?
網(wǎng)易云信團(tuán)隊(duì)對 language binding 有很多靈活的自定義需求:?
從實(shí)現(xiàn)層面:需要能夠自定義命名風(fēng)格、方法實(shí)現(xiàn)細(xì)節(jié)、業(yè)務(wù)邏輯的封裝等;
從接口易用性、友好性的角度:作為軟件提供商,需要保證 API 簡單易用,且符合語言的最佳實(shí)踐;
調(diào)研了當(dāng)前比較流行的同類工具后,發(fā)現(xiàn)它們在自定義代碼生成上的支持不夠,用戶很難對生成的代碼進(jìn)行控制,無法滿足上面提及的需求。為此云信團(tuán)隊(duì)結(jié)合自身需求研發(fā)了 NeCodeGen,通過代碼模板給予使用者對生成的代碼完全的控制,使之成為一個(gè)通用的、靈活的工具。
當(dāng)前開源世界中存在很多非常優(yōu)秀的自動(dòng)化生成 language binding 工具,比如強(qiáng)大的 SWIG、dart ffigen 等,NeCodeGen 的主要目標(biāo)是滿足靈活的自定義需求,能夠作為現(xiàn)有工具集的一個(gè)補(bǔ)充。在云信團(tuán)隊(duì)中,常常將它和其他代碼生成工具結(jié)合使用來提升研發(fā)效率,下面是云信的一個(gè)應(yīng)用場景:
?
由于 dart ffigen 只支持 C 接口,因此首先使用 NeCodeGen 開發(fā)生成 C API 和對應(yīng)的 C implementation 的應(yīng)用程序,然后使用 dart ffigen 由 C API 來生成的 dart binding,由于 dart ffigen 生成的 dart binding 大量使用 dart ffi 中的類型,它無法滿足易用性、友好性需求(上圖中將稱為low level dart binding)。還需要基于它進(jìn)一步進(jìn)行封裝,云信再次使用 NeCodeGen 生成更加友好易用的 high level dart binding,在實(shí)現(xiàn)上依賴 low level dart binding。
03 NeCodeGen 簡介
NeCodeGen 是一個(gè)代碼生成框架,它以 Python package 的方式發(fā)布,工程師可以基于它來開發(fā)自己的應(yīng)用,它的目的是簡化具有相同需求的用戶的開發(fā)成本,提供解決這類問題的最佳工程實(shí)踐,具備如下特性:
使用靈活: 內(nèi)置模板引擎 jinja,讓工程師使用 jinja 模板語言來靈活的描述代碼模板;
支持從 C++ 同時(shí)生成多種目標(biāo)語言程序,便于工程師同時(shí)管理多種目標(biāo)語言程序,這一點(diǎn)和 SWIG 類似;
提供最佳工程實(shí)踐;
充分利用 Python 的語法糖;
在實(shí)現(xiàn)上 NeCodeGen 使用 Python3 作為開發(fā)語言,使用 Libclang 作為 compiler front end,使用 jinja 作為模板引擎,它借鑒了:
在 Python 中非常流行的 web 框架 Flask;
clang 的 LibASTMatchers 和 LibTooling;
SWIG;
下文將對 NeCodeGen 的各個(gè)部分進(jìn)行更加詳細(xì)的介紹。
04 clang 的簡介
clang 是 LLVM project 的 C 系語言 compiler front end,它支持的語言包括: C、C++、Objective C/C++ 等。clang 采用的是“Library Based Architecture"”(基于 library 的架構(gòu)),這意味著它的各個(gè)功能模塊會以獨(dú)立的庫的方式實(shí)現(xiàn),工程師可以直接使用這些功能,并且 clang 的 AST 能夠完整的反映 source code 的信息。clang 的這些特性幫助了工程師基于它來開發(fā)一些工具,典型的例子就是 clang-format。網(wǎng)易云信的工程師在調(diào)研后選擇使用 clang 來作為 NeCodeGen 的 compiler front end。
05 工欲善其事,必先利其器: 學(xué)習(xí) clang AST
我們先做一些準(zhǔn)備工作: 學(xué)習(xí) clang AST,這是使用它來實(shí)現(xiàn)源到源轉(zhuǎn)譯工具的前提,如果讀者已經(jīng)掌握了 clang AST,可以跳過本段。clang AST 比較龐雜,從根本上來說這是源于 C++ 語言的復(fù)雜性,本節(jié)使用 Libclang 的 Python binding 帶領(lǐng)讀者以實(shí)踐探索的方式學(xué)習(xí) clang AST。
讀者首先需要安裝 Libclang 的 Python binding,命令如下:
pip install libclang為便于演示,不將 C++ code 保存到文件中,而是通過字符串的方式傳入到 Libclang 中進(jìn)行編譯,完整程序如下:
import clang.cindex code = """#include <string>/// test functionint fooFunc(){ return 1;}/// test classclass FooClass{ int m1 = 0; std::string m2 = "hello"; int fooMethod(){ return 1; }};int main(){ fooFunc(); FooStruct foo1; FooClass foo2;}""" # C++源代碼 index = clang.cindex.Index.create() # 創(chuàng)建編譯器對象 translation_unit = index.parse(path='test.cpp', unsaved_files=[('test.cpp', code)], args=['-std=c++11']) #index.parse 函數(shù)編譯 C++ code,參數(shù) args 表示編譯參數(shù)。
?Translation unit?
index.parse 函數(shù)的返回值類型為 clang.cindex.TranslationUnit(轉(zhuǎn)換單元),我們可以使用 Python 的 type 函數(shù)進(jìn)行驗(yàn)證:?
?type(translation_unit)?Out[6]: clang.cindex.TranslationUnit?查看 include?
for i in translation_unit.get_includes(): print(i.include.name)通過調(diào)用?get_includes()?可以查看 translation unit 所包含的所有的頭文件。如果讀者實(shí)際進(jìn)行執(zhí)行的話,會發(fā)現(xiàn)它實(shí)際包含的頭文件不止 <string>,這是因?yàn)轭^文件?<string>?會包含其他頭文件,而這些頭文件還會包好其他的頭文件,compiler 需要逐個(gè)包含。
?get_chidren?
clang.cindex.TranslationUnit 的 cursor 屬性表示它的 AST,我們來驗(yàn)證一下它的類型:
???????
?type(translation_unit.cursor)?Out[9]: clang.cindex.Cursor從輸出可以看出,它的類型是?clang.cindex.Cursor;它的成員方法??get_children()?可以返回它的直接子節(jié)點(diǎn):
???????
for child in translation_unit.cursor.get_children(): print(f'{child.location}, {child.kind}, {child.spelling}')輸出摘要如下:
???????
......<SourceLocation file 'D:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30133\\include\\string', line 24, column 1>, CursorKind.NAMESPACE, std<SourceLocation file 'test.cpp', line 4, column 5>, CursorKind.FUNCTION_DECL, fooFunc<SourceLocation file 'test.cpp', line 8, column 7>, CursorKind.CLASS_DECL, FooClass"......"表示省略了部分輸出內(nèi)容;仔細(xì)觀察最后四行,它們是文件?test.cpp?中的內(nèi)容,能夠和源代碼正確地匹配,這也驗(yàn)證了前面提及的:"clang AST 能夠完整的反映 source code 的信息"。
DECL 是“declaration”的縮寫,表示“聲明”。
?walk_preorder?
clang.cindex.Cursor 的 walk_preorder 方法對 AST 進(jìn)行先序遍歷:
???????
children = list(translation_unit.cursor.get_children()) foo_class_node = children[-2] # 選取 class FooClass 的節(jié)點(diǎn)樹for child in foo_class_node.walk_preorder(): # 先序遍歷? ? print(f'{child.location}, {child.kind}, {child.spelling}')上述對 class FooClass 對應(yīng)的 AST 進(jìn)行先序遍歷,輸出如下:
???????
<SourceLocation file 'test.cpp', line 8, column 7>, CursorKind.CLASS_DECL, FooClass<SourceLocation file 'test.cpp', line 9, column 9>, CursorKind.FIELD_DECL, m1<SourceLocation file 'test.cpp', line 9, column 14>, CursorKind.INTEGER_LITERAL, <SourceLocation file 'test.cpp', line 10, column 17>, CursorKind.FIELD_DECL, m2<SourceLocation file 'test.cpp', line 10, column 5>, CursorKind.NAMESPACE_REF, std<SourceLocation file 'test.cpp', line 10, column 10>, CursorKind.TYPE_REF, std::string<SourceLocation file 'test.cpp', line 11, column 9>, CursorKind.CXX_METHOD, fooMethod<SourceLocation file 'test.cpp', line 11, column 20>, CursorKind.COMPOUND_STMT, <SourceLocation file 'test.cpp', line 12, column 9>, CursorKind.RETURN_STMT, <SourceLocation file 'test.cpp', line 12, column 16>, CursorKind.INTEGER_LITERAL,請讀者自行將上述輸出和源代碼進(jìn)行對比。
?AST node: clang.cindex.Cursor?
對于 clang.cindex.Cursor,下面是它非常重要的成員:
kind,類型是 clang.cindex.CursorKind;
type,類型是 clang.cindex.Type,通過它可以獲得類型信息;
spelling,它表示節(jié)點(diǎn)的名稱。
05 jinja 模板引擎簡介
由于后面的例子中會使用 jinja,故先對它進(jìn)行簡單介紹。讀者不需要有學(xué)習(xí)新事物的惶恐,因?yàn)?jinja 非常簡單易學(xué),模板并不是什么新概念,熟悉模板元編程的讀者對于模板應(yīng)該不會陌生,并且 jinja 的模板語言和 Python 基本相同,因此并不會引入太多新的概念,一些 jinja 中的概念其實(shí)完全可以使用我們熟知的概念來進(jìn)行類比。
下面是一個(gè)簡單的 jinja 模板和渲染模板的程序:
from typing import Listfrom jinja2 import Environment, BaseLoader jinja_env = Environment(loader=BaseLoader)view_template = jinja_env.from_string( 'I am {{m.name}}, I am familiar with {%- for itor in m.languages %} {{itor}}, {%- endfor %}') # jinja模板class ProgrammerModel: """ model """ def __init__(self): self.name = '' # 姓名 self.languages: List[str] = [] # 掌握的語言 def controller(): xiao_ming = ProgrammerModel() xiao_ming.name = 'Xiao Ming' xiao_ming.languages.append('Python') xiao_ming.languages.append('Cpp') xiao_ming.languages.append('C') print(view_template.render(m=xiao_ming)) if __name__ == '__main__': controller()上面程序定義了一個(gè)簡單的軟件工程師自我介紹的模板 view_template,然后對其進(jìn)行渲染,從而得到完整的內(nèi)容,運(yùn)行程序,它的輸出如下:
I am Xiao Ming, I am familiar with Python, Cpp, C,?jinja template variable 其實(shí)就是 "模板參數(shù)"?
仔細(xì)對比 view_template 和最終的輸出,可以發(fā)現(xiàn)其中使用 {{ }} 括起來的部分會被替換,它就是 jinja template variable,即“模板參數(shù)”,它的語法為: {{template variable}}。
?MVC 設(shè)計(jì)模式?
在上面的程序中,其實(shí)我們使用了 MVC 設(shè)計(jì)模式:
?
在后面的程序中,還會繼續(xù)使用這種設(shè)計(jì)模式,NeCodeGen 非常推薦工程師使用這種設(shè)計(jì)模式來構(gòu)建應(yīng)用,在后面有專門的章節(jié)對 MVC 設(shè)計(jì)模式進(jìn)行介紹。
?jinja render 其實(shí)很像是“替換”?
view_template.render(m=xiao_ming) 即是對模板進(jìn)行渲染,這個(gè)過程可以簡單的理解為“替換”,即使用變量 xiao_ming 對模板參數(shù) m 進(jìn)行替換,如果使用函數(shù)參數(shù)來進(jìn)行類比的話,變量 xiao_ming 是實(shí)參。
06 Abstraction and code template?
當(dāng)程序中出現(xiàn)重復(fù)代碼的時(shí)候,我們最先想到的是泛型編程、元編程、注解等編程技巧,它們能夠幫助工程師簡化代碼,但不同的 programming language 的抽象能力不同,并且對于一些編程任務(wù)上述編程技巧也無濟(jì)于事。這些都導(dǎo)致了工程師不可避免地去重復(fù)寫相同模式的代碼,這種問題在實(shí)現(xiàn) language binding 中尤其突出。
對于這類問題,NeCodeGen 給出的解法是:
對于重復(fù)的代碼,工程師需要抽象出它們的通用模式(代碼模板),然后使用 template language 來描述代碼模板,在 NeCodeGen 中,使用的 template language 是 jinja;
NeCodeGen 會編譯源程序文件并生成 AST,工程師需要從 AST 中提取必要的數(shù)據(jù),然后執(zhí)行轉(zhuǎn)換(參見后面的“代碼轉(zhuǎn)換”章節(jié)),然后將轉(zhuǎn)換后的數(shù)據(jù)作為代碼模板中模板參數(shù)的實(shí)參完成了代碼模板的渲染,從而得到了目標(biāo)代碼。
下面就結(jié)合簡單的例子來對對上述解法進(jìn)行更加具體的說明,在這個(gè)例子中,工程師需要將 C++ 中的 struct 在 TypeScript 中進(jìn)行等價(jià)的定義,為清晰起見,下面以表格的形式展示了一個(gè)具體的例子:?
| C++ | TypeScrip |
| ? | ? |
現(xiàn)在我們需要考慮如何讓程序自動(dòng)化地幫我們完成這個(gè)任務(wù)。顯然通過 clang,我們可以拿到 struct NIM_AuthInfo 的 AST,我們還需要考慮如下問題:
Q1:C++ 類型和 TypeScript 類型的對應(yīng)關(guān)系?
A: std::string -> string,int -> integer
Q2:C++ 中 struct 在 TypeScript 中如何進(jìn)行命名?
A:為簡單起見,我們讓 TypeScript 中的名稱和 C++ 的保持一致。
Q3:TypeScript 中使用什么語法來描述類似于 C++struct?
A: 使用的 TypeScript interface 來進(jìn)行描述,我們可以使用 jinja 寫出通用的代碼模板來進(jìn)行描述。
下面我們給出具體的實(shí)現(xiàn)。按照前面的 MVC 章節(jié)提出的思想,我們可以首先建立 struct 的數(shù)據(jù)建模:
class StructModel: def __init__(self): self.src_name = '' # 源語言中的名稱 self.des_name = '' # 目標(biāo)語言的名稱 self.fields: List[StructFieldModel] = [] # 結(jié)構(gòu)體的字段 class StructFieldModel: def __init__(self): self.src_name = '' # 源語言中的名稱 self.des_name = '' # 目標(biāo)語言的名稱 self.to_type_name = '' # 目標(biāo)語言的類型名稱然后我們寫出 TypeScript 的代碼模板,這個(gè)代碼模板是基于 StructModel 來寫的:?
???????
export interface {{m.des_name}} {{% for itor in m.fields %}{{itor.des_name}} : {{itor.to_type_name}} ,{% endfor %}}接下來的工作就是從 C++ struct AST 中提取關(guān)鍵數(shù)據(jù)并進(jìn)行必要的轉(zhuǎn)換:
???????
def controller(struct_node: clang.cindex.Cursor, model: StructModel) -> str: model.src_name = model.des_name = struct_node.spelling # 提取struct的name for field_node in struct_node.get_children(): field_model = StructFieldModel() field_model.src_name = field_model.des_name = field_node.spelling # 提取字段的name field_model.to_type_name = map_type(field_node.type.spelling) # 執(zhí)行類型映射 model.fields.append(field_model) return view_template.render(m=model) # 渲染模板,得到TypeScript代碼?完整程序?
完整程序可以通過如下鏈接獲得:?
https://github.com/dengking/clang-based-src2src-demo-code
07 從源語言到目標(biāo)語言的轉(zhuǎn)譯
將由源語言編寫的程序轉(zhuǎn)譯為目標(biāo)語言的程序時(shí),主要涉及如下三個(gè)方面的轉(zhuǎn)換:
?類型轉(zhuǎn)換 type mapping?
從源語言中的類型到目標(biāo)語言中的類型的轉(zhuǎn)換。在 NeCodeGen 中對 C++ 語言的內(nèi)置類型和 C++ 標(biāo)準(zhǔn)庫類型進(jìn)行了枚舉并給出了預(yù)定義,對于這部分類型的轉(zhuǎn)換,使用 hash map 建立映射關(guān)系;對于用戶自定義類型,NeCodeGen 無法給出預(yù)定義,則需要由工程師自行定義。
?命名轉(zhuǎn)換 name mapping?
不同語言的命名規(guī)范不同,因此工程師需要考慮命名轉(zhuǎn)換。如果源程序遵循統(tǒng)一的命名規(guī)范,那么使用正則表達(dá)式能夠方便地進(jìn)行命名的轉(zhuǎn)換,這樣能夠保證生成的程序的嚴(yán)格的遵循用戶設(shè)置的命名規(guī)范,這也體現(xiàn)了自動(dòng)化代碼生成工具的優(yōu)勢:程序?qū)γ?guī)范的遵守比工程師更加嚴(yán)格。
?語法轉(zhuǎn)換 syntax mapping?
在網(wǎng)易云信的 NeCodeGen 中,語法轉(zhuǎn)換主要是通過代碼模板完成的,工程師需要按照目標(biāo)語言的語法來編寫代碼模板,然后通過渲染即可得到符合目標(biāo)語言語法的程序。
08 NeCodeGen 的 Design pattern
至此,讀者已經(jīng)對云信 NeCodeGen 有了一些基本認(rèn)識,本節(jié)主要介紹云信 NeCodeGen 推薦的一些設(shè)計(jì)模式,在云信 NeCodeGen 的實(shí)現(xiàn)中,提供了支持這些 design pattern 的基礎(chǔ)功能。這些設(shè)計(jì)模式是經(jīng)過工程實(shí)踐后總結(jié)得出的,能夠幫助工程師開發(fā)出更易維護(hù)的應(yīng)用,由于 C++ 語言的復(fù)雜性,其 AST 的處理也會比較復(fù)雜,合適的設(shè)計(jì)模式就尤為重要,這對于大型項(xiàng)目而言,具有重要意義。
?Matcher?
在編寫源到源轉(zhuǎn)譯工具時(shí),常用的模式是匹配感興趣的節(jié)點(diǎn),然后對匹配的節(jié)點(diǎn)執(zhí)行對應(yīng)的處理,比如名稱轉(zhuǎn)換、類型轉(zhuǎn)換。Matcher pattern 就是為這種典型的需求而創(chuàng)建的:框架遍歷 AST,并執(zhí)行用戶注冊的 match funcion(匹配函數(shù)),一旦匹配成功,則執(zhí)行 match funcion 對應(yīng)的 callback。這種模式是 clang 社區(qū)為開發(fā) clang tool 而總結(jié)出來的,并提供了支持庫? LibASTMatchers,關(guān)于此,讀者可以閱讀如下文章:?
-
https://clang.llvm.org/docs/LibASTMatchers.html#matching-the-clang-ast
-
https://clang.llvm.org/docs/LibASTMatchersTutorial.html
云信 NeCodeGen 借鑒了這種模式,并結(jié)合 Python 語言的特性、自身的需求進(jìn)行了本地化實(shí)現(xiàn),它運(yùn)用了 Python 的 decorator 語法糖,通用的寫法如下:
???????
@frontend_action.connect(match_func)def callback(): pass上述寫法的含義是: 告訴frontend_action連接(connect) match funcionmatch_func 和 callback ?callback;frontend_action 在遍歷 AST 時(shí),會將節(jié)點(diǎn)作為入?yún)?#xff0c;依次執(zhí)行所有向它注冊的 match func,如果 match func 返回 True,則表示匹配成功,框架就會執(zhí)行 callback 函數(shù)來對匹配成功的節(jié)點(diǎn)進(jìn)行處理,否則 pass。
通過實(shí)踐來看,這種模式能夠應(yīng)用的結(jié)構(gòu)更加清晰、代碼復(fù)用程度更高。
目前 clang 官方并沒有提供 LibASTMatchers 的 Python binding,為便于用戶使用,云信 NeCodeGen 提供對常用節(jié)點(diǎn)進(jìn)行匹配的 match funcion。
?MVC?
MVC 模式讀者應(yīng)該不會陌生,它是前端開發(fā)中經(jīng)常使用的一種模式,在本文前面的“jinja模板引擎簡介”章節(jié)中已經(jīng)對其進(jìn)行了簡單介紹,云信 NeCodeGen 中 MVC 可以歸納為:
?
實(shí)際使用中,推薦工程師使用自頂向下的思路:定義 model,確定 model 的成員,基于 model 來編寫代碼模板,然后再編寫提取、轉(zhuǎn)換函數(shù)來獲取數(shù)據(jù)來對 model 進(jìn)行初始化,最后使用 model 來渲染模板。
從實(shí)踐來看,MVC 能夠使代碼結(jié)構(gòu)清晰,維護(hù)性更強(qiáng);對于需要從一種源語言生成多種目標(biāo)語言程序的項(xiàng)目,MVC 能夠保證 Model 在目標(biāo)語言中保持一致,這在一定程度上能夠提示代碼復(fù)用。
?總結(jié)?
Matcher pattern 是 NeCodeGen 框架使用的模式,MVC pattern 則是推薦應(yīng)用開發(fā)者使用的模式;Matcher pattern 的 callback 對應(yīng)了 MVC pattern 的 controller,即工程師在 callback 中實(shí)現(xiàn) controller 的功能。
09 How NeCodeGen run
通過前面的介紹,我們已經(jīng)對?NeCodeGen?的運(yùn)行流程有了大致的認(rèn)識,下面是以流程圖的形式對?NeCodeGen?的運(yùn)行流程進(jìn)行總結(jié):
?
10 應(yīng)用價(jià)值
代碼生成工具的主要目的是提升生產(chǎn)力,對于大型項(xiàng)目而言,它的作用更加明顯。在網(wǎng)易云信的工程實(shí)踐中,工程師會綜合運(yùn)用多種代碼生成工具,充分發(fā)揮工具的威力,并將工具加入 CICD,這樣的做法極大地提升了研發(fā)效率;對生產(chǎn)力的提高還體現(xiàn)在重構(gòu)和維護(hù)上,在修改源語言程序后,運(yùn)行工具即可得到更新后的目標(biāo)語言程序,這能夠避免由于源語言程序和目標(biāo)語言程序的不一致而導(dǎo)致的錯(cuò)誤;重構(gòu)工作也將變得簡單,對目標(biāo)語言程序的重構(gòu)將簡化為修改代碼模板,重新運(yùn)行工具后,即可完成所有的重構(gòu)。代碼生成工具優(yōu)勢還體現(xiàn)在對代碼規(guī)范的遵守上,通過將命名規(guī)范、代碼規(guī)范等編碼在工具中,能夠保證生成的程序?qū)Υa規(guī)范百分之百的遵守。
NeCodeGen 除了可以應(yīng)用于 language binding 的生成上,還可以應(yīng)用于其他領(lǐng)域,比如實(shí)現(xiàn)類似于 QT 的 Meta-Object System,它也可以作為一個(gè) stub code generator。
?術(shù)語?
?參考內(nèi)容?
https://en.wikipedia.org/wiki/Comparison_of_code_generation_tools
?作者介紹?
開開,網(wǎng)易云信資深 C++ 開發(fā)工程師,負(fù)責(zé)云信基礎(chǔ)技術(shù)研發(fā),具有豐富的研發(fā)經(jīng)驗(yàn),熟悉編程語言理論。
總結(jié)
以上是生活随笔為你收集整理的技术宝典 | NeCodeGen:基于 clang 的源到源转译工具的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 来自开发者的点赞!网易云信揽获3大技术奖
- 下一篇: 来自开发者的点赞 · 网易云信揽获三大技