如何动态导入 zig 模块?
How to import zig modules dynamically?
我正在使用 zig 0.7.0.
并尝试从数组中导入 zig 源文件列表。每个源文件都有一个我想调用的 main
函数(其 return 类型为 !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 中可以进行这种动态导入吗?
我认为您想要的那种导入是可能的,我的理解是 @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
截至 Zig 0.8.0, the operand to @import
is 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 个文件并启动编译过程,甚至在 运行 编译时代码确定哪个文件之前一个实际上是进口的。双赢.
我正在使用 zig 0.7.0.
并尝试从数组中导入 zig 源文件列表。每个源文件都有一个我想调用的 main
函数(其 return 类型为 !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 中可以进行这种动态导入吗?
我认为您想要的那种导入是可能的,我的理解是 @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
截至 Zig 0.8.0, the operand to @import
is 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 个文件并启动编译过程,甚至在 运行 编译时代码确定哪个文件之前一个实际上是进口的。双赢.