Zenn
Closed5

Zigで条件付きコンパイル (zig 0.13.0)

funatsufumiyafunatsufumiya

C/C++でいう、以下のようなものをzigではどうすればいいのかについての調査。

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
   //define something for Windows (32-bit and 64-bit, this part is common)
   #ifdef _WIN64
      //define something for Windows (64-bit only)
   #else
      //define something for Windows (32-bit only)
   #endif
#elif __APPLE__
    #include <TargetConditionals.h>
    #if TARGET_IPHONE_SIMULATOR
         // iOS, tvOS, or watchOS Simulator
    #elif TARGET_OS_MACCATALYST
         // Mac's Catalyst (ports iOS API into Mac, like UIKit).
    #elif TARGET_OS_IPHONE
        // iOS, tvOS, or watchOS device
    #elif TARGET_OS_MAC
        // Other kinds of Apple platforms
    #else
    #   error "Unknown Apple platform"
    #endif
#elif __ANDROID__
    // Below __linux__ check should be enough to handle Android,
    // but something may be unique to Android.
#elif __linux__
    // linux
#elif __unix__ // all unices not caught above
    // Unix
#elif defined(_POSIX_VERSION)
    // POSIX
#else
#   error "Unknown compiler"
#endif
funatsufumiyafunatsufumiya

方法については以下の記事にあるように、if文の条件がcomptimeに決まる値だとして、もしコンパイル時にfalseになっていれば、そのif文の中身は解析対象およびコード生成から外れるという特性を活かし、このcomptimeの値やif文をプリプロセッサのマクロ(C言語でいう#define#if等)と同じようにして利用する。

https://mitchellh.com/writing/zig-comptime-conditional-disable

const builtin = @import("builtin");

fn myFunction() void {
    if (builtin.os.tag == .macos) {
        // This code will only be included if the target OS is macOS.
        return;
    }

    // This code will be included for all other operating systems.
}
funatsufumiyafunatsufumiya

実際に自分も実験してみた。@cInclude等を使うと実際のヘッダファイルを準備しなければいけなくて面倒なので、ここでは、@embedFile で好きなファイルを埋め込めることを活かし、ビルドオプションによってファイルの存在確認(つまり @embedFile の行全体)がきちんとスルーされるかを確認した。

https://github.com/funatsufumiya/zig-conditional-build-test/

funatsufumiyafunatsufumiya

READMEに書いている通りだけど、実際に書いたコードは以下の通り。

このコードは zig build run とすればファイルの埋め込みはされない(かつ埋め込み対象のファイルが存在していなくても問題ない)。そして zig build run -Denable_embed=true とすればファイル埋め込みされ、もちろんのことながらファイルが存在していなければコンパイル時にエラーを吐く。

main.zig

const std = @import("std");
const options = @import("build_options");

const enable_embed = options.enable_embed;

// Conditionally embed the file based on build option
const embedded_content: ?[]const u8 = if (enable_embed) 
    @embedFile("embed.txt") 
else 
    null;

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    try stdout.print("Hello from main!\n", .{});
    if (enable_embed) {
        try stdout.print("Content: {s}\n", .{embedded_content.?});
    }else{
        try stdout.print("Embedding is disabled by build option. If you want to enable it, set the build option `-Denable_embed=true`\n", .{});
    }
}

build.zig (抜粋)

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // Add a build option
    const embed_option = b.option(
        bool,
        "enable_embed",
        "Enable file embedding feature",
    ) orelse false;

    const options = b.addOptions();
    options.addOption(bool, "enable_embed", embed_option);

    const exe = b.addExecutable(.{
        .name = "zig_conditional_build_test",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    exe.root_module.addOptions("build_options", options);

    b.installArtifact(exe);

    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }
    run_step.dependOn(&run_cmd.step);
}
funatsufumiyafunatsufumiya

これによってわかることは、zigはプリプロセッサ・マクロ専用の構文を持っていない代わりに、コンパイル時計算ができ、かつ不必要な箇所は自動で無視するように作られているので、comptimeな値によって自由自在にプリプロセッサのような処理ができることがわかる。

(comptimeは、実用上はC++のテンプレートやRustのマクロ相当だと考えると良いけれど、zigは目に見えないフローの存在を嫌う言語なので、ASTの処理はできないことに注意。その代わりに、制約は多少あるが実行時と同じようにコンパイル時計算ができて値を返すことができ、かつZigは型を至って普通に値として扱うことができるので、それを活かしていくことになる。)

ただ、この特性のために、使われていない関数にエラーがあってもきちんと静的解析されず、エラーが内在するプログラムを書いてしまいがちな点に注意。(この点はコンパイル時ダックタイピングのようなことを許容するがゆえの、諸刃の剣。)

このスクラップは3ヶ月前にクローズされました
作成者以外のコメントは許可されていません