用 Golang 重写 rsync(2):方案的选择
当下要把一个 c 程序重写成 golang 的程序,不考虑你就是原作者,熟悉代码,直接开写之外,我认为对于一般的开发来讲,方案必须要是能够循序渐进的,这种循序渐进一是能够确保有中间步骤可以分解,例如保证编译可以随时进行,或者确保可以交叉进行测试。
所以我考察了两种方案:
第一个是用翻译器把 c 代码翻译成 go 语言。
第二个是用 cgo 一点点渗透。
C2GO
开始其实我觉得有翻译工具能够直接把 C 翻译成 Go 是很扯的事情,但是没想到这事居然还有底层逻辑:
The C code is preprocessed with clang. This generates a larger file (
pp.c
), but removes all the platform-specific directives and macros.
pp.c
is parsed with the clang AST and dumps it in a colourful text format that looks like this. Apart from just parsing the C and dumping an AST, the AST contains all of the resolved information that a compiler would need (such as data types). This means that the code must compile successfully under clang for the AST to also be usable.Since we have all the types in the AST it's just a matter of traversing the tree in a semi-intelligent way and producing Go. Easy, right!?
这段话的意思就是先利用 Clang 工具将 C 语言的代码进行整合和预编译,生成 AST(Abstract Syntax Tree 抽象语法树?),再对这个抽象语法树进行处理,反向生成 Golang 代码。
这样的逻辑,我觉得可行性就比较高了,语言对语言不好做,我觉得一个 Tree 对语言的反向翻译,还是有可行性的。
这段话摘自我采用的这个开源工具:
https://github.com/elliotchance/c2go
后来我还看见了另外一个类似的工具,不知道有没有人注意到,Golang 的 sqlite3 库实际是个 Cgo 的作品,我从 Mac 切回 Windows 之后,倒腾这个 C 环境就费了死劲,后来我就找纯 golang 的 sqlite3 库,打算把我的 Orm 依赖改成纯 Golang sqlite3 库,找到的库的名字叫
modernc.org/sqlite
我对这个名字产生了兴趣,modernc?现代 c 语言?于是我查了下,这个库背后还真有一个 c2go 的工具,原理也是将 AST 翻译成 Golang 程序,库是:
https://modernc.org/ccgo/v3
我自己用的是 c2go 这个工具,但是既然 ccgo 可以翻译 sqlite3,应该也是不错的,不过说起来,对于翻译 sync 这个量级的代码,用处不大,我先截个图给大家看看,具体的我们下期再讲
上面这个图,我们可以看到翻译的内容大量涉及指针,类型转换也比较复杂,总体来讲不容易理解。
Cgo
因为生成代码的质量问题,所以我用工具生成了一版之后,还是回来选择了用 Cgo 慢慢迭代,这个逻辑就很简单了,我的流程如下:
1)讲原 C 程序 main 入口包装成函数 main_wrapper()
2)写 Golang 的 main 函数直接调用 c 的封装函数 main_wrapper()
3)逐步将 main_wrapper()中的步骤重写到 Golang main 中。
4)过程中保持编译和测试,确保流程和数据正确
5)翻译过程中,涉及全局变量时,将涉及全局变量优先迁移到 Golang,在 C 中使用 setter 和 getter 函数替代原变量引用。
这个过程复杂,缓慢,但是感觉似乎也只有这条路。
版权声明: 本文为 InfoQ 作者【百家饭隐私计算平台创业者】的原创文章。
原文链接:【http://xie.infoq.cn/article/256e9e1ace56ea6d1fd3a4b95】。文章转载请联系作者。
评论