What is the correct way of using single-header C libraries from Zig?
I’m sorry for the long post, there must be a small thing I’m missing, but I’m really lost here.
Until recently, I was happily doing “direct approach”:
const c = @cImport(
{
@cDefine("RAYGUI_IMPLEMENTATION", {});
@cInclude("raygui.h");
});
Now it doesn’t work anymore, and I have decided to switch to the “official” (?) approach, which, I assume, looks like this: add a C wrapper for the “raygui.h” to the project. This file #defines "RAYGUI_IMPLEMENTATION"
and #includes "raygui.h"
.
However, I don’t understand how would I then call functions / use types defined in “raygui.h” from Zig.
Here’s a small example - my header library, shlib.h
:
#ifndef SHLIB_H
#define SHLIB_H
// C single-header library
int multiply(int a, int b)
{
return a * b;
}
typedef struct my_args_struct
{
int a;
int b;
} my_args;
int multiply_args(my_args args)
{
return args.a * args.b;
}
#endif
Using “direct approach”:
const std = @import("std");
const c = @cImport({ @cInclude("shlib.h"); });
pub fn main() void
{
std.debug.print("multiply returned {}\n", .{ c.multiply(2, 2) });
const args = c.my_args{ .a = 10, .b = 20 };
std.debug.print("multiply_args returned {}\n", .{ c.multiply_args(args) });
}
I compile it with zig build-exe direct.zig -idirafter ./
using 0.12.0-dev.819+75b48ef50
and it runs happily, which is strange, because it segfaults with my Zig Raylib examples.
Using “official approach”, I have to add my C wrapper (which is a simple one-liner, #include <shlib.h>
), to my compilation command, something like zig build-exe official.zig shlib_impl.c -idirafter ./
.
But then how do I call multiply()
, create an instance of my_args
or call multiply_args()
from Zig code?
If I simply invoke it as multiply(2, 2)
, Zig rightly complains about error: use of undeclared identifier 'multiply'
.
I also can’t @import("shlib_impl.c");
(shlib_impl.c is my C wrapper) or @cImport({ @cInclude("shlib_impl.c"); });