zig-cinterop について
zig-cinteropは、ZigとCの相互運用に取り組むエージェント向けのスキルです。C宣言の取り込みや関数の公開、ABI互換の型、ヘッダー変換、混在ビルドを扱います。
主な機能
- 01@cImportによるC宣言の取り込み
- 02translate-cでのヘッダー確認
- 03C型とABIレイアウトの対応
- 04Cから呼び出せるZig関数の公開
- 05C/Zig混在プロジェクトのビルド
活用シーン
- 01ZigからCライブラリを呼び出す
- 02ZigによるCヘッダーの変換を確認する
- 03Cの構造体やビットフィールドの配置を合わせる
- 04C側から呼べるZig関数を用意する
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill zig-cinteropインストール後、Claude Code で/zig-cinteropを入力して読み込まれたか確認します。
同梱ファイルreferences/translate-c-guide.md
詳細
Zig C 相互運用
目的
Zig の C 相互運用についてエージェントに説明します。C を呼び出すための @cImport/@cInclude、ヘッダーを調べるための translate-c、ABI 互換型を定義するための extern struct と packed struct、C から利用できる Zig のエクスポート、C と Zig を混在させたビルドのための zig cc を扱います。
トリガー
- 「Zig から C 関数を呼び出すにはどうすればよいですか?」
- 「@cImport と @cInclude はどのように使いますか?」
- 「C から呼び出せるように Zig 関数をエクスポートするにはどうすればよいですか?」
- 「C の構造体と一致する構造体を定義するにはどうすればよいですか?」
- 「translate-c は何をしますか?」
- 「C と Zig を混在させたプロジェクトをビルドするにはどうすればよいですか?」
ワークフロー
1. @cImport を使って Zig から C を呼び出す
const c = @cImport({
@cInclude("stdio.h");
@cInclude("string.h");
@cInclude("mylib.h");
@cDefine("MY_FEATURE", "1"); // Equivalent to -DMY_FEATURE=1
@cUndef("SOME_MACRO");
});
pub fn main() void {
_ = c.printf("Hello from C: %d\n", @as(c_int, 42));
var buf: [256]u8 = undefined;
_ = c.snprintf(&buf, buf.len, "formatted: %d", @as(c_int, 100));
}
build.zig 内:
exe.linkLibC(); // Required when using C functions
exe.addIncludePath(b.path("include/"));
2. translate-c — C ヘッダーの変換を調べる
translate-c は C ヘッダーを Zig の宣言に変換するため、Zig が C API をどのように認識するかを正確に確認できます。
# Translate a header file
zig translate-c /usr/include/stdio.h > stdio.zig
# Translate with defines/includes
zig translate-c -I include/ -DFEATURE=1 mylib.h > mylib.zig
# Translate and inspect specific types
zig translate-c mylib.h | grep -A5 "struct MyStruct"
これは Zig における bindgen に相当します。生成される内容を理解するために使い、その後、コード内で @cImport を直接使用します。
3. C 型のマッピング
| C 型 | Zig 型 |
|---|---|
int | c_int |
unsigned int | c_uint |
long | c_long |
unsigned long | c_ulong |
long long | c_longlong |
size_t | usize |
ssize_t | isize |
char * | [*:0]u8(ヌル終端) |
const char * | [*:0]const u8 |
void * | *anyopaque |
NULL | null |
bool | bool(C99)または c_int(旧バージョン) |
float | f32 |
double | f64 |
// Passing strings to C
const str = "hello";
_ = c.puts(str); // Zig string literals are [*:0]const u8
// Dynamic strings — need null terminator
var buf: [64:0]u8 = undefined;
const len = std.fmt.bufPrint(buf[0..63], "hello {d}", .{42}) catch unreachable;
buf[len] = 0;
_ = c.puts(&buf);
4. extern struct — ABI 互換の構造体
C の構造体とメモリレイアウトを完全に一致させるには、extern struct を使います。
// Matches: struct Point { int x; int y; };
const Point = extern struct {
x: c_int,
y: c_int,
};
// Matches: struct Header { uint32_t magic; uint16_t version; uint16_t flags; };
const Header = extern struct {
magic: u32,
version: u16,
flags: u16,
};
// Use with C API
var p = Point{ .x = 10, .y = 20 };
_ = c.draw_point(&p);
5. packed struct — ビット単位のレイアウト
// Matches C bitfield: struct { uint8_t flags : 4; uint8_t type : 4; };
const Flags = packed struct(u8) {
mode: u4,
kind: u4,
};
// Packed struct for wire protocols
const IpHeader = packed struct(u32) {
ihl: u4,
version: u4,
tos: u8,
total_length: u16,
};
var h: IpHeader = @bitCast(@as(u32, raw_bytes));
6. Zig を C にエクスポートする
// Export a function callable from C
export fn zig_add(a: c_int, b: c_int) c_int {
return a + b;
}
// Export with specific calling convention
pub fn my_func(x: u32) callconv(.C) u32 {
return x * 2;
}
// Export a struct (use extern struct for C layout)
export const VERSION: c_int = 42;
C ヘッダーを生成します(手動またはツールを使用):
/* mylib.h */
#ifndef MYLIB_H
#define MYLIB_H
#include <stdint.h>
int zig_add(int a, int b);
uint32_t my_func(uint32_t x);
extern int VERSION;
#endif
7. build.zig で C から Zig を呼び出す
// Build Zig as a C-compatible static library
const lib = b.addStaticLibrary(.{
.name = "myzig",
.root_source_file = b.path("src/lib.zig"),
.target = target,
.optimize = optimize,
});
// The C code that uses the Zig library
const c_exe = b.addExecutable(.{
.name = "c_consumer",
.target = target,
.optimize = optimize,
});
c_exe.addCSourceFile(.{
.file = b.path("src/main.c"),
.flags = &.{"-std=c11"},
});
c_exe.linkLibrary(lib);
c_exe.linkLibC();
b.installArtifact(c_exe);
8. 不透明型と前方宣言
// Forward-declared C struct (opaque)
const FILE = opaque {};
extern fn fopen(path: [*:0]const u8, mode: [*:0]const u8) ?*FILE;
extern fn fclose(file: *FILE) c_int;
extern fn fprintf(file: *FILE, fmt: [*:0]const u8, ...) c_int;
// Opaque handle pattern
const MyHandle = opaque {};
extern fn lib_create() ?*MyHandle;
extern fn lib_destroy(h: *MyHandle) void;
9. 可変長引数関数
// Call variadic C functions using @call with variadic args
const c = @cImport(@cInclude("stdio.h"));
// printf works directly through @cImport
_ = c.printf("value: %d\n", @as(c_int, 42));
// For custom variadic C functions, use extern with ...
extern fn my_log(level: c_int, fmt: [*:0]const u8, ...) void;
translate-c の出力ガイドと C ABI 型のリファレンスについては、references/translate-c-guide.md を参照してください。
関連スキル
zig ccによる C コンパイルと基本的な Zig ビルドには、skills/zig/zig-compilerを使用します- C と Zig を混在させたプロジェクトの
build.zigには、skills/zig/zig-build-systemを使用します - シンボルのエクスポートと ABI の確認には、
skills/binaries/elf-inspectionを使用します - Rust の C FFI アプローチとの比較には、
skills/rust/rust-ffiを使用します
コメント
Markdown 対応