[英]How to import zig modules dynamically?
我正在使用 zig 0.7.0.
我正在嘗試從數組中導入 zig 源文件列表。 每個源文件都有一個我想調用的main
function (其返回類型為!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
塊之外記錄它們。
您可以修復它的另一種方法是通過使用內聯 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 開始@import
@import 的操作數必須是字符串文字。
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 個文件並啟動編譯過程,甚至在運行編譯時代碼以確定實際導入哪個文件之前。 雙贏。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.