C Interop, Modules and Interfaces

I’m just start writing a 2D videogame with Zig and SDL3. I’ve got a couple of question about how to structure my code and use of allocators.

  1. Since I’m using SDL3, I first thought using std.heap.c_allocator was the way to go, but I checked and SDL gives a way to override the alloc/dealloc functions. My understanding though is that if I want to use the GPA, I need to add the block length to each allocation because the GPA wants slices, correct?

  2. I want to hide the SDL implementation so that I can switch something else in if in the future I want to implement a few things myself, so at the moment I have actually three modules in my build.zig, the one referencing main.zig, “core” which has platform independent code and “platform” which has the SDL implementation (I also want to add a “Null” platform for testing), where “platform” depends on “core” and is selected by a build switch which defaults to sdl. As it’s a game and I’m not interested in replacing the platform impl at runtime, I think this is quite neat, but do you have any alternatives?

  3. Since zig has no interface concept (a la rust traits), is there a way I can enforce a particular shape for struct? I know the compiler would tell me if I call or reference something missing, but I was hoping to have a complete interface check (I’ve found zig-interface, that seems to do that). Again any idiomatic way to do this?

Thanks everyone, I’m finding this community is great!

you can do what c allocators commonly do, put a header on each allocation that describes the length.

but it would be easier to not override SDLs allocator

Regardless, your zig code doesn’t need to use the same allocator as sdl! in which case a clear boundary on which allocator to use is helpful e.g all your stuff uses your zig allocator, and only sdl things can use sdls allocator.

std.heap.c_allocator does more than call libc directly, so it is not interchangeable with libc allocator api; you’d need to be careful regardless.

I can’t see that being useful for normal testing, which should be on self-contained and isolated portions of code.

I can see it being useful for running simulations to find invalid states in your game.

yes, with normal zig logic at comptime, e.g if (!@hasField(T, "foo") @compileError("T has no field 'foo'")

But I recommend avoiding that for most things, as your error message will often be worse than zigs as it will only have the text you provide (you can ++ concatenate text, or std.fmt.comptimePrint, for fancier messages).

So reserve that for things that would not otherwise cause a compile error, and similar situations e.g. if T.foo needs to be an integer large enough to fit value 300, otherwise it could in some runtime situations overflow and panic.

3 Likes

Thanks @vulpesx,
My second question was more about the use of the build system module, the null platform as you said will be used for running simulation that I consider testing anyway. My intention is someday to write also the part covered by sdl so I could switch the sdl platform with mine (or at least have an option to do that easily).

As for the third point, so I take it an approach like zig-interfaces is not common.

Thank you again!