I just had plain c.sdl* functions and I decided that I want to strip them to sdl.*

Gnerally it is just about trying to find a way to create decls for comptime generated struct (As a probably a lot of people tried: example1, example2). And I found one way!

In zig you can do following:

// It is definitely not ofraid of runtime light...
const NotVampire = struct {
    pub fn test_fn() void {}
    // Just formally it could be runtime. We see it
    // but now it is comptime only type because of this field 
    i_am_variable_i_swear: @TypeOf(test_fn) = test_fn,
    variable: u8,
};

But you when try check the @typeInfo(NotVampire).@"struct".field_names you will see the i_am_variable_i_swear and will be able to check it is type and etc. So I understood that it can be abused used for my purposes

It creates kind of “illegal” and very restrained structs but it CREATES NEW STRUCTS WITH FUNCTIONS!. Because I am so excited it seems to me someone already did this before me (maybe even in the linked threads, I am not sure)…

Also if you are not following rules (won’t mention them) you are going to either scare the Maker process with horrible code or outright kill it and get SIGSEGV (this part probably should be reported as bug :thinking:)

const std = @import("std");

const Amogus = struct {
    fn amogus_speech() void {
        std.debug.print("Amogus speech: I am normal crewmate. Don't kill me\n", .{});
    }
};
const Normal = struct {
    pub fn test_fn() void {}
    i_am_variable_i_swear: @TypeOf(test_fn) = test_fn,
    arg: u8,
};
pub fn main(init: std.process.Init) !void {
    // To show that main wasn't just wholly optimized to comptime
    var buf: [1024]u8 = undefined;
    var stdin_reader = std.Io.File.stdin().reader(init.io, &buf);
    const stdin = &stdin_reader.interface;
    const input = try stdin.takeByte();

    // const amogus: Amogus = .{};
    // amogus.amogus_speech(); // Illegal: amogus_speech is not a field or member function

    // Amogus.amogus_speech(); // Legal

    // Impostor().amogus_speech(); // Illegal: amogus_speech is actualy a field (which exist only for instances not for type itself)

    // const impostor: Impostor() = .{ .variable = input }; // Illegal: comptime-only type can't store runtime value

    const impostor: Impostor() = .{ .variable = 32 };
    impostor.amogus_speech();
    impostor.impostor_speech(input);
    std.debug.print("{}\n", .{impostor.variable});

    // Illegal, when you add any non `.@"comptime" = true` fields but you still can instanciate it
    std.debug.print("Size of impostor: {}\n", .{@sizeOf(Impostor())});
}

fn Impostor() type {
    const anon = struct {
        fn impostor_speech(runtime: u8) void {
            std.debug.print("It's not SIGSEGV. I am born! Muahahahahahaha!\n", .{});
            std.debug.print("I had also ripped off a part of amogus: `amogus_speech()`\n", .{});
            std.debug.print("I know runtime values: {c}\n", .{runtime});
            std.debug.print("I have variables, but don't know how to access them\n", .{});
            std.debug.print("And I don't know my runtime size\n", .{});
            std.debug.print("I exist but I am crippled (ㅠ﹏ㅠ)\n", .{});
        }
    };
    const info = @typeInfo(struct {}).@"struct";
    const new_names = [_][]const u8{ "amogus_speech", "impostor_speech", "variable" };
    const new_types = [_]type{ @TypeOf(Amogus.amogus_speech), @TypeOf(anon.impostor_speech), u32 };
    const new_attrs = [_]std.lang.Type.Struct.FieldAttributes{
        .{ .@"comptime" = false, .default_value_ptr = Amogus.amogus_speech },
        .{ .@"comptime" = false, .default_value_ptr = anon.impostor_speech },
        .{ .@"comptime" = false },
    };
    return @Struct(info.layout, info.backing_integer, &new_names, @ptrCast(&new_types), @ptrCast(&new_attrs));
}

P.S It is more like “showcase” but I am not sure. It seems to be more suitable here… I found a way to not only shoot my legs but also legs of compiler :laughing:. And yeah, please tell me if it was really already known

pasta July 17, 2026, 12:35pm 2

This does seem like it’s going a little too hard when a simpler solution may be available, what problem are you trying to solve? This appears like a question of how to auto-generate bindings, where it may be more sensible to generate zig as part of the build process than use comptime like this.

Can you show a concrete example of the c.sdl* functions you want to be able to use as sdl.*?

ashley.h July 17, 2026, 12:39pm 3

Yeah, I know thank you. I just wanted to shoot my legs. It is not like I can write even 10 lines of normal code, I am probably even more crippled than my code so if something is said is unpractical/hacky/impossible, I just have fun with it and shoot my legs and turns out legs of compiler too…

pasta July 17, 2026, 12:50pm 4

Happy to help with some fun!

But at least for me this needs something more concrete - what are you trying to do? I ask because without definition it seems like this is all you need:

const sdl = struct {
    const foo = c.sdlFoo;
};

But given the complexity of exploration you seem to want more fun than that. Are you trying to auto-generate the sdl namespace from what’s in c? If so, what is in your c? What are your constraints?

Exactly. But I want to do it in comptime without code generators :grin:

Yeah, I want to strip whole c. Translate all functions and all variables/structs and etc to new names without prefix. I generally already mostly (very sparsely but I need mostly to figure out one “bug” and bring everything together) did for superficial structs of my own, it is now time to just test it in a wild…

Well, to be preciseI don’t even need sdl anymore, I trashed it out and will just render to memory and save as file… But you know vulkan seems not so easy target as sdl so I decided to start from sdl… So yeah I have only vulkan now in c and nothing else

It is perfect because c namespace is going to be comptime so no problems with no runtime support :slight_smile: