登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> CLQ工作室开源代码 >> 主题: zig 两篇     [回主站]     [分站链接]
标题
zig 两篇
clq
浏览(393) + 2022-12-23 14:49:18 发表 编辑

关键字:

[2022-12-23 14:53:36 最后更新]
zig 两篇

Zig学习篇|zig调用c语言标准库函数
https://zhuanlan.zhihu.com/p/459281215

https://www.zhihu.com/question/50121841



clq
2022-12-23 14:53:35 发表 编辑

你以为只有C++能在项目中加入C代码,实现混C编程?NO,NO,NO,zig也行。
[user@linux 01_cfuns]$ tree
.
├── build.zig
└── src
├── cfuncs.c
├── cfuncs.h
└── main.zig

1 directory, 4 files

[user@linux 01_cfuns]$ cat src/cfuncs.c
#include "cfuncs.h"

int numAddTen(int v) {
return v+10;
}

[user@linux 01_cfuns]$ cat src/cfuncs.h
#ifndef __C_FUNCS_H__
#define __C_FUNCS_H__

extern int numAddTen(int);

#endif

[user@linux 01_cfuns]$ cat src/main.zig
const std = @import("std");

const cfuncs = @cImport({
@cInclude("cfuncs.h");
});

pub fn main() anyerror!void {
const orig_val = 33;
var result_val = cfuncs.numAddTen(orig_val);
std.log.info("The value return by c function is {}", .{result_val});
}

[user@linux 01_cfuns]$ cat build.zig
...
const exe = b.addExecutable("01_funcs", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();

exe.linkLibC();
exe.addIncludeDir("src");
exe.addCSourceFile("src/cfuncs.c", &[_][]const u8{"-Wall"});
...
________________________________________
你以为只有C++能在struct加方法?NO,NO,NO,zig也行。
const std = @import("std");

const MyType = struct {
my_var: i32,

fn varAdd(self: *MyType, val: i32) i32 {
self.my_var += val;
return self.my_var;
}
};

pub fn main() anyerror!void {
var my_data = MyType {
.my_var = 30,
};

_ = my_data.varAdd(10);

std.log.info("All your codebase are belong to us. {}", .{my_data});
}
rust的关联函数
,或者说C++的静态方法,zig也支持。
const std = @import("std");

const MyModule = struct {
var do_add = true;
var my_var: i32 = 0;

fn setDoAdd(setting: bool) void {
do_add = setting;
}

fn varAdd(val: i32) i32 {
if (do_add) {
my_var += val;
} else {
my_var -= val;
}
return my_var;
}
};

pub fn main() anyerror!void {
MyModule.setDoAdd(false);
_ = MyModule.varAdd(10);
MyModule.setDoAdd(true);
_ = MyModule.varAdd(30);

std.log.info("All your codebase are belong to us. {}", .{MyModule.my_var_val});
}
相对来说,还是rust的 impl Type {} 和 Type::associated_function() 语法更直观,容易阅读理解。
struct的成员都是公开的,但每个zig文件都是一个struct,同时zig文件的函数和类型默认都是非公开的,所以可以通过把struct内容写到一个独立zig文件实现一定程度的隐藏(成员变量无法隐藏)。
________________________________________
你以为只有go语言有go命令?NO,NO,NO,zig也有zig命令。
[user@linux 03_zig_cmd]$ zig -h
Usage: zig [command] [options]

Commands:

build Build project from build.zig
init-exe Initialize a `zig build` application in the cwd
init-lib Initialize a `zig build` library in the cwd

ast-check Look for simple compile errors in any set of files
build-exe Create executable from source or object files
build-lib Create library from source or object files
build-obj Create object from source or object files
fmt Reformat Zig source into canonical form
run Create executable and run immediately
test Create and run a test build
translate-c Convert C code to Zig code

ar Use Zig as a drop-in archiver
cc Use Zig as a drop-in C compiler
c++ Use Zig as a drop-in C++ compiler
dlltool Use Zig as a drop-in
dlltool.exe
lib Use Zig as a drop-in lib.exe
ranlib Use Zig as a drop-in ranlib

env Print lib path, std path, cache directory, and version
help Print this help and exit
libc Display native libc paths file or validate one
targets List available compilation targets
version Print version number and exit
zen Print Zen of Zig and exit

General Options:

-h, --help Print command-specific usage
zig init-exe自动生成构建脚本。跟go的go.mod和rust的Cargo.toml不同,zig生成的构建脚本也是zig程序。
[user@linux 03_zig_cmd]$ cat build.zig
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("03_zig_cmd", "src/main.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);
}
这点致敬了Kotlin用Gradle Kotlin DSL构建的做法。
________________________________________
你以为只有go语言的编译器是交叉编译器
?NO,NO,NO,zig编译器也是。
[user@linux 03_zig_cmd]$ zig build -Dtarget=x86_64-windows-gnu

[user@linux 03_zig_cmd]$ file zig-out/bin/03_zig_cmd.exe
zig-out/bin/03_zig_cmd.exe: PE32+ executable (console) x86-64, for MS Windows
跟go相似,zig可以通过OS/CPU虚拟层运行交叉编译出来的程序。譬如当我安装了wine,就可以运行交叉编译出来的win32程序:
[user@linux 03_zig_cmd]$ zig build run -Dtarget=x86_64-windows-gnu -fwine
0084:fixme:hid:handle_IRP_MN_QUERY_ID Unhandled type 00000005
0084:fixme:hid:handle_IRP_MN_QUERY_ID Unhandled type 00000005
0084:fixme:hid:handle_IRP_MN_QUERY_ID Unhandled type 00000005
0084:fixme:hid:handle_IRP_MN_QUERY_ID Unhandled type 00000005
0084:fixme:wineusb:query_id Unhandled ID query type 0x5.
info: All your codebase are belong to us.
zig支持的虚拟层包括了
[user@linux 03_zig_cmd]$ zig build -h
...
-fdarling, -fno-darling Integration with system-installed Darling to
execute macOS programs on Linux hosts
(default: no)
-fqemu, -fno-qemu Integration with system-installed QEMU to execute
foreign-architecture programs on Linux hosts
(default: no)
--glibc-runtimes [path] Enhances QEMU integration by providing glibc built
for multiple foreign architectures, allowing
execution of non-native programs that link with glibc.
-frosetta, -fno-rosetta Rely on Rosetta to execute x86_64 programs on
ARM64 macOS hosts. (default: no)
-fwasmtime, -fno-wasmtime Integration with system-installed wasmtime to
execute WASI binaries. (default: no)
-fwine, -fno-wine Integration with system-installed Wine to execute
Windows programs on Linux hosts. (default: no)
...
zig支持的交叉编译目标包括。。。太多了,从单片机到IBM大型机到WASM、renderscript,从linux、ios到UEFI、到无操作系统(即裸机开发)都全包。可以通过下面命令查看:
zig targets
zig默认构建Debug模式的程序,可以通过选项选择release-safe、release-fast、release-small这三种模式。开发者可以在不同开发阶段
选用适当的构建模式。
[user@linux 03_zig_cmd]$ zig build -h
...
-Drelease-safe=[bool] Optimizations on and safety on
-Drelease-fast=[bool] Optimizations on and safety off
-Drelease-small=[bool] Size optimizations on and safety off
...
________________________________________
你以为只有Rust有Option?NO,NO,NO,zig也有。
const std = @import("std");

fn myFunc(dividend: u32, divisor: u32) ?u32 {
if (divisor == 0) {
return null;
} else {
return dividend / divisor;
}
}

fn tryMyFunc(dividend: u32, divisor: u32) void {
var my_result = myFunc(dividend, divisor);

if (my_result) |result| {
std.log.info("get result: {}\n", .{result});
} else {
std.log.info("get no result\n", .{});
}
}


const GlobalArray = [_]i8 { -1, -2, -3, -4, -5};

fn myFunc2(idx: u32) ?i8 {
if (idx > 4) {
return null;
} else {
return GlobalArray[idx];
}
}

fn tryMyFunc2(idx: u32) void {
var my_val = myFunc2(idx);

if (my_val) |value| {
std.log.info("get value {}\n", .{value});
} else {
std.log.info("get no value\n", .{});
}
}

pub fn main() anyerror!void {
tryMyFunc(10, 2);
tryMyFunc(10, 0);

tryMyFunc2(0);
tryMyFunc2(1000);
}
zig指针是非空的,所以要表示可空指针必须用Option。
const std = @import("std");

const ListNode = struct {
next: ?*ListNode,
data: i32,
};

pub fn main() anyerror!void {
var node2 = ListNode{.next = null, .data = 300};
var node1 = ListNode{.next = &node2, .data = 200};
var head = ListNode{.next = &node1, .data = 100};

var curr_node: ?*ListNode = &head;
var idx : i32 = 0;
// zig的for语句是foreach,所以要使用c语言那样的for语句只能用while
while (curr_node != null) : ({curr_node = curr_node.?.next; idx += 1;}) { // zig也没有++操作符,只能用+=1
std.log.info("data of node {} is {}", .{idx, curr_node.?.data}); // Option值必须使用 .? 来unwrap
}
}
________________________________________
你以为只有Rust有Result?NO,NO,NO,zig也有(error)。
const std = @import("std");

const ArithmeticError = error{DividedByZero};

fn myFunc(dividend: u32, divisor: u32) ArithmeticError!u32 {
if (divisor == 0) {
return ArithmeticError.DividedByZero;
} else {
return dividend / divisor;
}
}

fn tryMyFunc(dividend: u32, divisor: u32) void {
var my_result = myFunc(dividend, divisor);

if (my_result) |result| {
std.log.info("get result: {}\n", .{result});
} else |err| switch(err) {
ArithmeticError.DividedByZero => std.log.info("get no result: {}\n", .{"divided by zero"}),
else => std.log.info("Unknown error: {}\n", .{err})
}
}


const AccessError = error{OutOfBound};
const SomeError = error{Error1, Error2};

const GlobalArray = [_]i8 { -1, -2, -3, -4, -5};

fn myFunc2(idx: u32) !i8 { // 省略错误类型表示可以返回任意类型的错误
if (idx > 4) {
return AccessError.OutOfBound;
} else if (idx > 2) {
return SomeError.Error1;
} else {
return GlobalArray[idx];
}
}

fn tryMyFunc2(idx: u32) void {
var my_val = myFunc2(idx);

if (my_val) |value| {
std.log.info("get value {}\n", .{value});
} else |err| switch(err) {
AccessError.OutOfBound => std.log.info("get no value: {}\n", .{"index out of bound"}),
else => std.log.info("Unknown error: {}\n", .{err})
}
}

pub fn main() anyerror!void {
tryMyFunc(10, 2);
tryMyFunc(10, 0);

tryMyFunc2(0);
tryMyFunc2(3);
tryMyFunc2(1000);
}
除了if之外,zig还有两种处理错误的形式——try和catch。catch表示捕获错误,并做处理(也可以直接忽略)。try表示当调用的函数返回错误时直接返回,并把错误返回给上一层函数。这两者可以看作是if处理的便捷方式。
const std = @import("std");
const fs = std.fs;

pub fn main() anyerror!void {
std.log.info("invoke function", .{});

var ret = fs.makeDirAbsolute("/tmp/test_dir/test_dir_2") catch |err| {
std.log.info("invoke function return error {}", .{err});
};

std.log.info("ret = {}, invoke second time", .{ret});

ret = try fs.makeDirAbsolute("/tmp/test_dir/test_dir_2");

std.log.info("done! ret = {}", .{ret});
}
从上面程序的运行结果可以看到,假如main函数
返回错误,zig会把错误打印到控制台,并且会打印调用栈以显示错误是从哪里发出的。
________________________________________
你以为只有go语言有defer?NO,NO,NO,zig也有。
zig不但有defer,还有errdefer(但没有okdefer)。
const std = @import("std");
const fs = std.fs;

fn readFile(file_name: []const u8) !void {
const func_name = "readFile";
std.log.debug("+++ {s}", .{func_name});
defer std.log.debug("--- {s}", .{func_name});
errdefer |err| std.log.debug("--- {s} error {}!", .{func_name, err});

var file = try fs.openFileAbsolute(file_name, .{});
defer {
std.log.info("now close file", .{});
file.close();
}

var buf = [_]u8{0} ** 100; // 成员为100个0的数组
_ = try file.read(buf[0..100]);

std.log.info("read \"{s}\"", .{buf});
}

pub fn main() anyerror!void {
_ = try readFile("/tmp/myfile.txt");

std.log.info("done!", .{});
}
________________________________________
你以为只有go语言有interface?NO,NO,NO,zig也有(手写的)。
zig没有直接的interface语法,但通过tagged union可以实现interface的功能。没有自带语法那么便捷,但当然你可以在调用时加些额外的处理。
const std = @import("std");

const Cat = struct {
name: []const u8,

pub fn talk(self: Cat) void {
std.debug.print("{s}: Meow!\n", .{self.name});
}
};

const Dog = struct {
name: []const u8,

fn talk(self: Dog) void {
std.debug.print("{s}: WangWang!\n", .{self.name});
}
};

const Animal = union(enum) {
cat: Cat,
dog: Dog,

fn talk(self: Animal) void {
switch (self) {
// zig >= 0.10
// inline else => |case| case.talk(),

// zig < 0.10
.cat => |cat| cat.talk(),
.dog => |dog| dog.talk(),
}
}
};

fn myFunc(animal: Animal) void {
animal.talk();
}

pub fn main() anyerror!void {
var tom_cat = Cat{ .name = "Tom" };
var wangcai_dog = Dog{ .name = "WangCai" };

var animal = Animal{ .cat = tom_cat };
myFunc(animal);

animal = Animal{ .dog = wangcai_dog };
myFunc(animal);
// or:
// myFunc(.{ .dog = .{ .name = "WangCai" } });
}
可能你会说interface的用法不是先定义一个interface,甚至写好一些使用该interface的函数,后面哪个struct实现了interface的方法就可以直接传给那些函数用吗?
是的,确实如此,要实现这样的用法就要自己构造虚函数表
,并且在实现interface的struct里面显式生成一个定义该interface的struct,非常繁琐。
并且由于要保留实际struct的指针和虚表,interface struct必须有成员变量。即使把interface struct写到一个文件里也没办法对这些成员变量做隐藏的,所以其实不是那么安全。
Interface语法是zig 1.0前我最希望看到能实现的功能。
cat src/Animal.zig
const std = @import("std");

const Animal = @This();

ptr: *anyopaque,
vtable: *const VTable,

const VTable = struct {
func1: fn (ptr: *anyopaque, arg1: usize, arg2: usize) void,
func2: fn (ptr: *anyopaque, arg1: []const u8) void,
};

pub fn func1(self: Animal, arg1: usize, arg2: usize) void {
std.debug.print("Animal {s}: ", .{"func1"});
self.vtable.func1(self.ptr, arg1, arg2);
}

pub fn func2(self: Animal, arg1: []const u8) void {
std.debug.print("Animal {s}: ", .{"func2"});
self.vtable.func2(self.ptr, arg1);
}

pub fn init(pointer: anytype,
comptime func1_: fn (ptr: @TypeOf(pointer), arg1: usize, arg2: usize) void,
comptime func2_: fn (ptr: @TypeOf(pointer), arg1: []const u8) void,
) Animal {
const Ptr = @TypeOf(pointer);
const ptr_info = @typeInfo(Ptr);

std.debug.assert(ptr_info == .Pointer); // Must be a pointer
std.debug.assert(ptr_info.Pointer.size == .One); // Must be a single-item pointer

const alignment = ptr_info.Pointer.alignment;

const gen = struct {
fn func1Impl(ptr: *anyopaque, arg1: usize, arg2: usize) void {
const self = @ptrCast(Ptr, @alignCast(alignment, ptr));
return @call(.{ .modifier = .always_inline }, func1_, .{ self, arg1, arg2 });
}

fn func2Impl(ptr: *anyopaque, arg1: []const u8) void {
const self = @ptrCast(Ptr, @alignCast(alignment, ptr));
return @call(.{ .modifier = .always_inline }, func2_, .{ self, arg1 });
}

const vtable = VTable{
.func1 = func1Impl,
.func2 = func2Impl,
};
};

return Animal{
.ptr = pointer,
.vtable = &gen.vtable,
};
}

cat src/main.zig
const std = @import("std");

const Animal = @import("Animal.zig");

fn myFunc(animal: Animal) void {
animal.func1(100, 200);
animal.func2("hello");

std.debug.print("{}\n", .{animal.vtable}); // oh, no,成员变量无法隐藏
}

const Cat = struct {
name: []const u8,

fn func1(self: *Cat, arg1: usize, arg2: usize) void {
std.debug.print("{s}: func1({}, {})!\n", .{self.name, arg1, arg2});
}

fn func2(self: *Cat, arg1: []const u8) void {
std.debug.print("{s}: func2({s})!\n", .{self.name, arg1});
}

fn animal(self: *Cat) Animal {
return Animal.init(self, func1, func2);
}
};

const Dog = struct {
name: []const u8,

fn func1(self: *Dog, arg1: usize, arg2: usize) void {
std.debug.print("{s}: func1({}, {})!\n", .{self.name, arg1, arg2});
}

fn func2(self: *Dog, arg1: []const u8) void {
std.debug.print("{s}: func2({s})!\n", .{self.name, arg1});
}

fn animal(self: *Dog) Animal {
return Animal.init(self, func1, func2);
}
};

pub fn main() anyerror!void {
var tom_cat = Cat{ .name = "Tom" };
var wangcai_dog = Dog{ .name = "WangCai" };

var animal = tom_cat.animal();
myFunc(animal);

animal = wangcai_dog.animal();
myFunc(animal);
}
________________________________________
你以为只有C++的容器类可以传入内存分配器?NO、NO、NO,zig所有需要分配内存的函数都需要你传入内存分配器。
那你会问了,那岂不是要先写一个内存分配器才能开始写zig程序?当然不会,zig标准库已经提供了几个内存分配器。譬如:
• 自带内存泄漏检测的通用内存分配器——std.heap.GeneralPurposeAllocator
• 记录分配地址,结束时一次性释放的Arena分配器——std.heap.ArenaAllocator
• 从固定buffer分配内存的FixedBuffer分配器——std.heap.FixedBufferAllocator
• 打印其它内存分配器的分配释放调用的logging分配器——std.heap.LoggingAllocator
• c库分配器——std.heap.c_allocator / std.heap.raw_c_allocator
• 直接采用系统调用
• 的page分配器——std.heap.page_allocator(wasm和裸机环境特别好用)
下面我们来看看GeneralPurposeAllocator、ArenaAllocator、LoggingAllocator怎么使用。
const std = @import("std");

fn strJoint(allocator: std.mem.Allocator, str1: []const u8, str2: []const u8) ![]const u8 {
const len = str1.len + str2.len;

var result_str = try allocator.alloc(u8, len);

std.mem.copy(u8, result_str, str1);
std.mem.copy(u8, result_str[str1.len..], str2);

std.log.info("{} + {} => {}", .{str1.len, str2.len, result_str.len});

return result_str;
}

fn makeLeak(allocator: std.mem.Allocator) !void {
_ = try allocator.alloc(u8, 100);
_ = try allocator.alloc(u8, 200);
}

pub fn main() anyerror!void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer std.log.info("memory leaked: {}", .{gpa.deinit()}); // gpa.deinit()会检测内存泄漏,并打印泄漏点
var gpa_allocator = gpa.allocator();

makeLeak(gpa_allocator) catch {}; // 制造泄漏

var arena = std.heap.ArenaAllocator.init(gpa_allocator); // arena使用gpa_allocator做内存申请和释放
defer arena.deinit(); // ArenaAllocator记录内存申请,在deinit()时逐一释放
var arena_allocator = arena.allocator();

var logging_allocator = std.heap.loggingAllocator(arena_allocator); // logging_allocator记录arena_allocator的函数调用
var my_allocator = logging_allocator.allocator();

var str = try strJoint(my_allocator, "全世界无产者", "联合起来!");
std.log.info("{s}", .{str});
my_allocator.free(str); // 无必要的,因为arena.deinit()会释放它们

str = try strJoint(my_allocator, "世界人民", "大团结万岁!");
std.log.info("{s}", .{str});
}
________________________________________
编辑于 2022-12-13 02:31



总数:1 页次:1/1 首页 尾页  
总数:1 页次:1/1 首页 尾页  


所在合集/目录



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1