I’m in the inital stages of rewriting a Database system I wrote in C in Zig with the intention of providing a C API to replace the old system but I’m not sure of the best way to handle Io in this.
Pretty much all IO operations now go through the Io layer and for the Zig module its trivial to just allow passing an Io object explicitly, for the C API this isn’t possible because it doesn’t have that Io layer, what would be the best way to do Io in this case?
If I were to simply target mainstream Zig targets like Linux then a global Io.Threaded instance would be good enough but I also target devices that require a custom Io implementation so thats sadly not an option.
I could just add a build option which then makes it pick between different Io implementations but if someone else needs their custom then they would be required to edit the code, is there any good way to deal with this?
One idea I have had is a Callback function implemented Zig that takes a opaque pointer and modifies it as if it were an Io pointer, that way someone could add their own zig file that implements only 1 function and get the functionality needed.
My first thought as well but the fact that the Io struct isn’t extern makes it difficult to pass around without ugly hacky workarounds.
Maybe the best option is to make it importable and then simply check if root has a global io implementation by name i.e. @hasField(root, "default_io")
That way I can default to Io.Threaded and replace it in a minimal Zig project, still kind of ugly though.
You can have pointers to auto layout structs in extern structs. You only have to make extern version of the std.Io. In this case C callconv does not matter since its being used by zig code anyways. C ABI is used as the ABI boundary.
And inside of the buildsystem description, I would just pass the “use_custom_io” option down to your code with false as a default.
My code would then look something like this:
const config = @import("config");
var global_io: if (config.use_custom_io) @import("custom_io") else std.Io.Threaded = undefined;
And of course I would document that the user can chose the io implementation by giving the "db" module an import called "custom_io" which follows the necessary interface.