Go+ c2go: 近2个月 C 标准库的迁移进展
不知不觉离我们最后一次报告 c2go 的进展又 2 个月过去了。在这两个月里,最重要的当然是 Go+ v1.1 的正式发布。在接下来的 Go+ v1.2 里,我们认为最重要的是这样 3 件事情:
Go+ ClassFile(类文件)机制的转正
Go+ c2go 工程化,至少完成 sqlite3 迁移
Go/Go+ 混合工程增强:支持调用 Go 泛型
在这两个月里,c2go 继续迭代,并在今天发布了 c2go v0.7.9 及 libc v0.3.11。今天我们就介绍一下这次发布都更新了什么。
c2go v0.7.9:语言更新
我们先来看看 c2go 对语言特性相关的更新。它主要包括:
1、支持了 CompoundLiteralExpr(复合字面量)。简单说,就是支持定义数组常量,结构体常量等。我们以数组常量为代表看个例子就清楚了:
#include <stdio.h>void foo(unsigned short a[]) {...}void main() {foo((unsigned short[3]){ 0x330e, 0, 16 });}
结构体(struct)或联合体(union)也是类似的。我们看一个联合体的例子:
#define aslong(v) ((union{double _fval; long _ival;}){v})._ival这是将一个双精度浮点数转为长整型的例子。这个转换并不是基于数值的,而是基于内存表示的。可以想象,aslong(1.1) 得到的结果是非常古怪的,并不是通过常规的简单数学运算得到。
2、支持 C 内建函数 __builtin_expect。这个关键字是 gcc 引入用来做编译器优化的,这里不详细展开。
3、支持 locale types(本地类型)。在 C 语言中,多个源代码文件允许出现相同名字的类型,但是其具体的类型却并不一致。比如 foo.c 中有 cookie 类型,如下:
struct cookie {int a;};
bar.c 中也有 cookie 类型,如下:
struct cookie {double a;int b;};
在 C 语言中并不会报 cookie 类型重复定义。究其原因,在 C 语言中类型只是内存布局的显式表达,更像是编译器方便我们操作内存的助记符,并不会影响链接器(linker)的链接工作。
4、消除 division by zero 错误(除零错)。在 C 语言中,我们在代码中写 0/0.0 等价于 NaN,写 1/0.0 等价于 Inf。但是如果我们原封不动转为 Go 代码编译阶段就会报 division by zero 错误。
解决思路有两种,一种是把 0/0.0 转为 func() float64 { return 0 }() / 0.0,这样就可以绕过 Go 编译器的 division by zero 检查。另一种则是直接将 0/0.0 转为 math.NaN()。我们选择了前者。
5、消除 static symbol 提前声明导致的链接错误。例如对于如下代码:
static double pone(double);double f(double v) {return pone(v);}static double pone(double x) {return 0;}
我们之前版本会把第一个 pone 声明和第二个 pone 实现认为是两个函数,进而导致 f 函数引用的 pone 函数指向第一个 pone 函数并且未实现。
究其原因,是因为在不同文件中声明的同名 static 函数会被 C 认为属于不同的函数,但在之前我们的实现中处理成在同一个文件中多次声明的 static 函数也被认为是不同函数,从而导致了该 bug。
c2go v0.7.9:命令行增强
另外,本次更新 c2go 命令行工具(它等价于 gop c 子命令)也做了不少功能增强,下面一一展开。
1、c2go.cfg 的源代码列表(source)支持忽略文件。在此前,源代码列表支持我们指定目录(dirs)和文件(files),并允许忽略其中的一些函数(ignore.names)不进行编译。
现在,我们新增了忽略文件(ignore.files)功能。这个功能主要是针对我们源代码目录中的文件比较多,我们希望编译其中绝大多数文件,只是对其中的少量文件进行忽略。例如:
{"source": {"ignore": {"files": ["./src/stdio/puts.c","./src/stdio/popen.c"]},"dirs": ["./src/math","./src/stdio"]},...}
这个 c2go.cfg 配置文件告诉编译器去编译 ./src/math 和 ./src/stdio 这两个目录,但是忽略其中的 ./src/stdio/puts.c, ./src/stdio/popen.c 这 2 个文件。
2、c2go.cfg 增加了 template cmds(命令模板)功能。这主要是 libc 的单元测试是以 cmd 形式(可执行程序)存在的,这导致 libc test 工程有非常多的 cmds,为了批量转换这些项目,故引入 template cmds 功能。
常规情况下,c2go.cfg 定义 cmds 的方式是这样的:
{"target": {"cmds": [{"dir": "cmd/test_qsort","deps": ["github.com/goplus/libc/test/common","github.com/goplus/libc"],"source": {"files": ["./src/functional/qsort.c"]}},{"dir": "cmd/test_string","deps": ["github.com/goplus/libc/test/common","github.com/goplus/libc"],"source": {"files": ["./src/functional/string.c"]}},{"dir": "cmd/test_string_memcpy","deps": ["github.com/goplus/libc/test/common","github.com/goplus/libc"],"source": {"files": ["./src/functional/string_memcpy.c"]}},{"dir": "cmd/test_string_memset","deps": ["github.com/goplus/libc/test/common","github.com/goplus/libc"],"source": {"files": ["./src/functional/string_memset.c"]}}]},...}
引入 template cmds 功能后,它可以改写为:
{"target": {"cmds": [{"for": ["qsort","string","string_memcpy","string_memset"],"dir": "cmd/test_$(it)","deps": ["github.com/goplus/libc/test/common","github.com/goplus/libc"],"source": {"files": ["./src/functional/$(it).c"]}}]},..。}
这让我们在测试案例非常多的时候可以轻松应对。
3、c2go.cfg 增加了 libc、simpleProj 全局标志。这主要是为了适应不同库对 C 内建函数的链接需求。
对于 libc 自身来说,内建函数的实现就在自身的包里面。而对于其他包来说,一般是通过 libc.XXX 来引用。但在少量测试环境下,我们选择人肉实现 C 内建函数,这时我们通过 simpleProj = true 来标识。
4、c2go 命令行增加了 -run 参数用于运行 c2go.cfg 中的某个 cmd。例如对于上面这个例子,我们执行
c2go -run cmd/test_string .就可以编译并执行 test_string 这个命令了。这对于 libc 的迁移来说非常有用,我们可以单独去执行某种类型的单元测试。
libc v0.3.11:标准库增强
本次 C 标准库的更新最主要的就是进行了大规模的翻译。之前我们 libc 的迁移工作的进度大概只有 5% 左右,当前则预期达到了 20-25% 这个级别。
在本次更新前,libc 的迁移工作是一个文件一个文件来的,我们主要通过它来验证一些典型的函数(例如 printf)迁移没有问题。但现在我们开始以目录为单位进行迁移。本次主要迁移的有:
./src/string(字符串相关,全部完成)
./src/ctype(字符相关,全部完成)
./src/prng(随机数相关,全部完成)
./src/math(数学相关,除 ./src/math/sqrtf.c 外全部)
./src/stdio(标准输入输出,少量文件未完成)
测试案例的迁移当前还较少,主要新增了:
github.com/goplus/libc/test/cmd/test/random
github.com/goplus/libc/test/cmd/test/strtod
由于新翻译了很多代码但是测试没更少,本次发布 libc 的测试覆盖率从 30% 附近下跌到了 13%。下一步我们将对测试覆盖率进行增强。
欢迎大家通过给 Go+ 和 c2go 代码仓库 Star 来对我们进行支持。相关的代码仓库为:
Go+ 源代码:github.com/goplus/gop
c2go 源代码:github.com/goplus/c2go
C 标准库迁移到 Go:github.com/goplus/libc
另外,也有不少小伙伴私信留言希望能够进 Go+ 用户群参与讨论。如果你感兴趣可以扫描以下二维码加入: