Zig从XX到放弃(1)C/C++/Zig混合工程
Zig
Zig是一门野心有点大,语法有点别扭,目前还在0.10版本的系统编程语言。
都不记得我是怎么开始拉开网站看了几眼的。问就是加班到人脑宕机,问就是扩展视野,问就是寻找创新灵感。
具体的介绍就不抄了,团队的中文化工作做的不错,说明版本开发的速度超慢……
有意思的特点
Zig有几个很有意思的地方:
- 只有一个可执行文件
zig.exe
; - 可以作为C语言工具链,
zig cc main.c
; - 可以作为C++工具链,
zig c++ main.cpp
; - 可以作为Zig的工具链,
zig main.zig
; - 语言自带构建系统,Zig构建系统,并且构建程序用zig语言编写,
zig build
; - 自带单元测试,
zig build test
。
稍微看一看,可玩性非常强。
目前的缺点:文档相对薄弱。
C/C++/Zig混合工程
概貌
稍微一看,我就想做一个玩具工程,这个玩具工程中:一个模块用c编写,一个模块用Zig编写,一个模块用c++编写。
这个玩具就是测量一段代码的运行时间,用微秒来计算,分辨率是纳秒。准备用c做一个被测函数,用zig做一个被测函数,测量时间的代码用c++完成(chrono很好用)。
安装zig之后,创建一个工程:
zig init-exe
增加几个目录和模块,最后的目录结构如下。
其中可以zig帮助创建的文件包括:
- build.zip:构建用的文件;
- src:源代码目录;
- src/main.zig:可执行程序
自行增加的文件包括:
- src/include:头文件
- src/sum:c模块
- src/timeit:c++模块
- fabinaci.zip:zig模块
主程序
主程序的逻辑非常简单:
- 导入标准库中的输出函数;
- 导入zig模块fabinaci;
- 导入c语言模块sum;
- 导入c++模块timeit;
- 编写用C语言调用模式的两个被测函数;
- 主函数:测试两个函数的运行时间,打印出来。
const print = @import("std").debug.print;
// Zig package
const fabinaci_zig = @import("fabinaci").fabinaci;
// c package
const sum_c = @cImport({
@cInclude("sum.h");
});
// c++ package
const timeit_cpp = @cImport(@cInclude("timeit.h"));
fn fabinaci23() callconv(.C) void {
_ = fabinaci_zig(20);
}
fn sum_test() callconv(.C) void {
_ = sum_c.sum(10, 20);
}
pub fn main() !void {
print("{} + {} = {}\t\t{d:>12.3} ms\n", .{ 20, 10, sum_c.sum(10, 20), timeit_cpp.time_it(sum_test) });
print("fabinaci({}) = {}\t{d:>12.3} ms\n", .{ 20, fabinaci_zig(20), timeit_cpp.time_it(fabinaci23) });
}
跟一般教程中多出来的就是callconv(.C)
,这里限定了函数的调用方式。
fn sum_test() callconv(.C) void {
_ = sum_c.sum(10, 20);
}
Zig模块
代码乏善可陈。据说递归调用可能会堆栈溢出。
pub fn fabinaci(n: u128) u128 {
if (n == 0) return 0;
if (n == 1) return 1;
return fabinaci(n - 1) + fabinaci(n - 2);
}
C语言模块
没错,这是几行侮辱智商的代码。
头文件:
#ifndef __SUM_IT_H
#define __SUM_IT_H
int sum(int x, int y);
#endif /* __TIME_IT_H */
.c文件
#include "sum.h"
int sum(int x, int y)
{
return x + y;
}
C++模块
头文件:
#ifndef __TIME_IT_H
#define __TIME_IT_H
// time the execution of the code
float time_it(void(*f)(void));
#endif /*__TIME_IT_H*/
cpp文件:
#include <chrono>
#ifdef __cplusplus
extern "C" {
#endif
#include "timeit.h"
float time_it(void(*f)(void))
{
auto start = std::chrono::high_resolution_clock::now();
f();
auto end = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count() * 1e-3;
}
#ifdef __cplusplus
}
#endif
构建脚本
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("hello-world", "src/main.zig");
exe.linkLibCpp();
exe.addIncludePath("src/include");
exe.addCSourceFiles(&.{ "src/sum/sum.c", "src/timeit/timeit.cpp" }, &[_][]const u8{});
exe.addPackagePath("fabinaci", "src/fabinaci.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();
const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const exe_tests = b.addTest("src/main.zig");
exe_tests.setTarget(target);
exe_tests.setBuildMode(mode);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&exe_tests.step);
}
这个脚本在系统默认的文件中,增加了几行。
exe.linkLibCpp();
exe.addIncludePath("src/include");
exe.addCSourceFiles(&.{ "src/sum/sum.c", "src/timeit/timeit.cpp" }, &[_][]const u8{});
exe.addPackagePath("fabinaci", "src/fabinaci.zig");
第一行是链接C++的库文件,这样才能使用chrono;第二行是设置包含文件目录;第三行增加两个文件(c和c++);第四行,增加zig文件(这里第一个参数就是包的名字)。
结果
zig build run
即可以运行:
总结
- Zig看起来挺好玩,编译时计算、内存管理、所见即所得;
- 工具使用起来也比C和C++要现代很多很多倍,我已经很多年不用C和C++就是工具链的品质实在是太上古……
- 整个概念相当完美,下一个正经的不正经工程可以用下Zig。