Translating enums from C

I’m having some trouble updating a function to construct enums from C libraries. I’d like to automatically translate the keyboard event structure from SDL3.

fn buildEnumFromC(comptime import: anytype, comptime prefix: []const u8) type {
    comptime var names: [][]u8 = &[_][]u8{};
    comptime var values: []u32 = &[_]u32{};

    var count = 0;
    inline for (std.meta.declarations(import)) |decl| {
        if (decl.name.len < prefix.len + 1) continue;
        
        @setEvalBranchQuota(1000000);
        if (std.mem.eql(u8, decl.name[0..prefix.len], prefix)) {
            comptime var name = [_]u8{ 0 } ** 100;
            std.mem.copyForwards(u8, &name, decl.name[prefix.len..]);
            const value = @field(import, decl.name);
            names = @constCast(names ++ &[_][]u8{ &name });
            values = @constCast(values ++ &[_]u32 { value });
            count += 1;
        }
    }
    
    return @Enum(
        u32,
        .nonexhaustive,
        names,
        values[0..count],
    );
}

pub const Keycode = buildEnumFromC(sdlKeycode, "SDLK_");

This compiles, but I get an error reporting that the enum values are not found:

src/main.zig:100:26: error: enum 'root.buildEnumFromC(cimport,"SDLK_"[0..5])' has no member named 'RIGHT'
                        .RIGHT => {
                        ~^~~~~
src/root.zig:33:12: note: enum declared here
    return @Enum(

I’m not sure how to diagnose this, any attempts at comptime reflection or printing the values have failed to compile. Any tips on how to understand the enum that is generated? Errors before that suggested that the correct number of enum values are being generated, but I can’t find any way to debug what the tags actually are.

Thanks

Use @compileLog to debug it.

Note that slices ending with ‘\0’ and those without ‘\0’ are not the same.

In your example, the name of the enum value that is actually written is “RIGHT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00”

Edit: This one is ok:

fn EnumFromC(comptime import: anytype, comptime prefix: []const u8) type {
    comptime var names: []const []const u8 = &[_][]const u8{};
    comptime var values: []const u32 = &[_]u32{};

    inline for (std.meta.declarations(import)) |decl| {
        if (decl.name.len < prefix.len + 1) continue;

        @setEvalBranchQuota(1000000);
        if (std.mem.eql(u8, decl.name[0..prefix.len], prefix)) {
            const name: []const u8 = decl.name[prefix.len..];
            const value = @field(import, decl.name);
            names = names ++ &[_][]const u8{name};
            values = values ++ &[_]u32{value};
        }
    }

    return @Enum(
        u32,
        .nonexhaustive,
        names,
        values[0..names.len],
    );
}
3 Likes

use ``` instead of > on lines before and after code, otherwise its hard to read

2 Likes

That solves it. Thanks for the tip about @compileLog by the way, I was driving myself insane on that one.