floooh / sokol-zig

Zig bindings for the sokol headers (https://github.com/floooh/sokol)
zlib License
341 stars 46 forks source link

Custom c definitions #27

Closed Manuzor closed 1 year ago

Manuzor commented 1 year ago

I'm trying to provide a custom definition for SOKOL_LOG but the only simple way I could come up with was editing sokol_defines.h directly. That's not ideal since that file is overwritten with every update of the library.

What I currently do is edit sokol_defines.h like so:

// sokol_defines.h
#define SOKOL_ZIG_BINDINGS
#define SOKOL_NO_ENTRY
#define SOKOL_LOG(msg) sokol_log_callback(msg)
#if defined(_WIN32)
    #define SOKOL_WIN32_FORCE_MAIN
    // #define SOKOL_LOG(msg) OutputDebugStringA(msg)
#endif
// FIXME: macOS Zig HACK without this, some C stdlib headers throw errors
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif

And then in my zig code that statically links sokol, I do

export fn sokol_log_callback(msg: [*c]const u8) void {
    std.log.scoped(.sokol).err("{s}", .{msg});
}

It would be nice if there was a way to customize sokol from build.zig. One way would be to provide an option for a path to your own sokol_defines.h and fall back to the default one if not specified.

// Sidenote: Maybe consider replacing some of these parameters with a struct, which could then also have default-initialized options like the backend.
pub fn buildSokol(b: *Builder, [...], comptime defines_file: ?[]const u8) *LibExeObjStep {
    // ...
    lib.addCSourceFile(sokol_path ++ csrc, &[_][]const u8{"-DIMPL", backend_option, "-DSOKOL_DEFINES=" ++ (defines_file orelse (sokol_path ++ "sokol_defines.h"))});
    // ...
}

And then in all the sokol_*.c files, use it like this:

// sokol_gfx.c
#if defined(IMPL)
#define SOKOL_GFX_IMPL
#endif
#include SOKOL_DEFINES
#include "sokol_gfx.h"

But, to be honest, I feel like you would want to provide this customizability per sokol library. So maybe let the user provide their own versions of sokol_*.c? Or even just a single custom sokol.c that would include all sokol headers as needed.

Another way I thought about was "fixing" up the LibExeObjStep instance produced by buildSokol, iterating over all link_objects, pull out the c files, and replace them with my own. But that felt pretty hacky so I didn't even try this route.

Thoughts?

floooh commented 1 year ago

I think the best solution would be to provide a runtime callback in the C APIs, same as I did somewhat recently with the allocator functions, which were previously also preprocessor defines, but now work like this:

https://github.com/floooh/sokol/blob/01eeb35658fee9b6fe2e31145925fbd5e22faf47/sokol_gfx.h#L663-L690

Manuzor commented 1 year ago

I agree, moving stuff out of the build or preprocessor into the API itself sounds like the ideal solution. It looks like this doesn't only affect logging, though. Skimming through sokol_gfx.h there's SOKOL_ASSERT, for example, which would have the same problem. But it sounds doable without changing too much around.

On the other hand, there's also the option to define SOKOL_DEBUG to force-enable debug stuff in release mode. This one sounds very useful but looks like it has to be specified at build-time. And there's probably more like that (SOKOL_TRACE_HOOKS?). Any plans or thoughts for that?

By the way, thanks for the fast reply and your excellent work on these headers and language bindings. ❤️

floooh commented 1 year ago

Ok, PR has been merged, closing this ticket :) (the sokol-zig bindings will be updated automatically)