我正在使用zig 0.7.0.,并且我正在尝试从数组中导入一组zig源文件。每个源文件都有一个我想要调用的main函数(其返回类型为!void)。数组module_names在编译时是已知的。
下面是我尝试做的事情:
const std = @import("std");
const log = std.log;
const module_names = [_][]const u8{
"01.zig", "02.zig", "03.zig", "04.zig", "05.zig",
};
pub fn main() void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
for (module_names) |module_name, i| {
const module = @import(module_name); // this fails
log.info("i {}", .{i});
try module.main();
}
}即使数组在编译时是已知的,@import(module_name)也会给我这个错误:
./src/main.zig:13:32: error: unable to evaluate constant expression
const module = @import(module_name);
^
./src/main.zig:13:24: note: referenced here
const module = @import(module_name);如果数组是动态生成的并且只在运行时知道,我可以理解错误,但在这里,module_names数组在编译时是已知的。所以我有点困惑...
或者,我还尝试将整个main主体包装在一个comptime块中:
pub fn main() void {
comptime {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
for (module_names) |module_name, i| {
const module = @import(module_name); // no errors here
log.info("i {}", .{i});
try module.main();
}
}
}在这里,@import(module_name)没有给我任何错误,但是log.info失败了,并显示了另一个错误:
/home/jack/.zig/lib/zig/std/mutex.zig:59:87: error: unable to evaluate constant expression
if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)
^
/home/jack/.zig/lib/zig/std/mutex.zig:65:35: note: called from here
return self.tryAcquire() orelse {
^
/home/jack/.zig/lib/zig/std/log.zig:145:60: note: called from here
const held = std.debug.getStderrMutex().acquire();
^
/home/jack/.zig/lib/zig/std/log.zig:222:16: note: called from here
log(.info, scope, format, args);
^
./src/main.zig:26:21: note: called from here
log.info("i {}", .{i});这种动态导入在zig中是可能的吗?
发布于 2020-12-07 01:53:00
我认为你想要的那种导入是可能的,我的理解是@import接受一个zig源文件并将其转换为结构类型。它实际上看起来甚至可以在运行时完成(尽管不使用@import,它需要一个comptime参数(这是问题的重要部分)。
你的第一个例子失败的原因是,你传递的参数,module_name在comptime中是未知的,直到你的程序运行,你的for循环才会执行。
你解决问题的直觉是正确的,让循环在编译时求值(特别是捕获值和迭代器);我认为有两件事可以解决它。
像您所做的那样,将循环包装在comptime块中是可行的,但是您需要考虑在编译时计算表达式到底意味着什么,以及它是否有意义。我认为log的实现将阻止您在编译时记录日志,因此您需要在循环内收集您感兴趣的值,并在comptime块之外记录它们。
另一种修复方法是在编译时强制计算循环的捕获值,方法是使用inline for展开循环
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
inline for (module_names) |module_name, i| {
const module = @import(module_name);
log.info("i {}", .{i});
try module.main();
}
}免责声明:可能有更好的方法来做这件事,我是语言的新手=D
发布于 2021-12-01 03:21:14
从Zig 0.8.0开始,@import的操作数是required to be a string literal。
Zig编译器想要知道所有可能导入的文件,以便当您启动编译过程时,它可以立即找到它们并对其进行编译。通过使快速编译器的存在成为可能,语言的设计受到了限制。
那么我们能做什么呢?我认为这以一种等效的方式完成了任务:
const std = @import("std");
const log = std.log;
const modules = struct {
pub const module_01 = @import("01.zig");
pub const module_02 = @import("02.zig");
pub const module_03 = @import("03.zig");
pub const module_04 = @import("04.zig");
pub const module_05 = @import("05.zig");
};
pub fn main() void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
inline for (@typeInfo(modules).Struct.decls) |decl, i| {
const module = @field(modules, decl.name);
log.info("i {d}", .{i});
try module.main();
}
}这里的巧妙之处在于,实际上,编译器能够急切地获取所有5个文件并启动编译过程,甚至在运行编译时代码以确定实际导入哪个文件之前。双赢。
https://stackoverflow.com/questions/65157883
复制相似问题