Not with translate-c and that's largely because the translate-c feature of the zig compiler is first and foremost responsible for use via @cImport in zig files.
But you can with comptime reflection of the @cImport.
Typically if I'm building a wrapper around a C lib and I want to warp it with a zig version that defines off the C version.
const c = @cImport({ @cInclude("foo.h"); });
pub const Foo = struct {
pub const Flags = enum(u16) {
A = c.FOO_OPTS_A,
B = c.FOO_OPTS_B,
//...
};
};
Although you could go step further and build this with comptime since you can build struct, union or enum, types that have no decls.
Given this header file:
enum Foo {
FOO_OPTS_A = 1,
FOO_OPTS_B = 2,
FOO_OPTS_C = 3,
FOO_OPTS_D = 4,
};
This zig code will compile it into an enum that's usable like if you hand rolled it.
const std = @import("std");
const c = @cImport({
@cInclude("enum.h");
});
fn buildEnumFromC(comptime import: anytype, comptime prefix: []const u8) type {
comptime var enum_fields: [1024]std.builtin.Type.EnumField = undefined;
comptime var count = 0;
inline for (std.meta.declarations(import)) |decl| {
if (decl.name.len < prefix.len + 1) {
continue;
}
@setEvalBranchQuota(10000);
if (std.mem.eql(u8, decl.name[0..prefix.len], prefix)) {
enum_fields[count] = .{
.name = decl.name[prefix.len + 1 ..],
.value = @field(import, decl.name),
};
count += 1;
}
}
return @Type(.{ .@"enum" = .{
.tag_type = u16,
.fields = enum_fields[0..count],
.decls = &.{},
.is_exhaustive = true,
} });
}
const FooOptions = buildEnumFromC(c, "FOO_OPTS");
pub fn main() void {
const flags: FooOptions = .A;
switch (flags) {
.A => std.debug.print("Got A\n", .{}),
.B => std.debug.print("Got B\n", .{}),
.C => std.debug.print("Got C\n", .{}),
.D => std.debug.print("Got D\n", .{}),
}
}
The best part is, because we can declare it as exhaustive ourselves, if there is any change to the C header file, adding or removing values will cause the compiler to error out on the switch in main.