Busy
I didn’t really have the chance to work on much or write today. I wanted to finish my Raylib bindings from yesterday, but I’ll have to put that up tomorrow.
For now, since it’s 11pm, I’ll just share an overview of what I did.
Overview
The basic setup goes like this:
- Generate XML for Raylib with Doxygen
- Parse the Doxygen output with
xmerl
- Translate to Zig
That’s pretty much it. It’s pretty simple, and not very rigid, but Raylib doesn’t change that often, and it’s not a very big library anyway. Making small adjustments as needed is pretty easy.
Doxygen
This isn’t the best way to do this. The only reason I picked Doxygen is because I’m familiar with it. It is not the correct tool for the job. Don’t do this.
Anyway, how you do it is just run doxygen -g
, enable
XML generation, and run doxygen
. Then you get a nice
raylib_8h.xml
which has most of the definitions you’ll
need. Struct definitions are in other files, but you don’t actually
need them, since Zig and Zigler both make this nice anyway. I ended up
parsing almost everything, like defines
,
typedefs
, etc.
xmerl
Once you have that, you need to parse the XML with
xmerl
into some kind of shape. This is pretty
straightforward. I used xmerl_xpath
by itself, which is a
little awkward. Using SweetXML
would’ve been a bit nicer,
I think, but I’ve been trying to get more familiar with existing tools
in OTP.
There’s not much else that went into this. A bit finicky, since I
don’t think xmerl
is actually spec compliant, but I
could’ve just been doing things wrong.
Translate
Next it’s just translating the C to Zig. For the most part, the translation ends up just being this:
pub const some_fn = raylib.SomeFn;
Which is really nice.
For “problem” types, it’s a little more involved. I still haven’t
settled on how I want to handle it, since the way I’m doing it with
Raylib is not very general. The basic behavior I have now is to just
define a list of problem types, like const char *
, and if
I see one of them in the parameters or return type, I translate all
the types to their Zig equivalent.
This is mostly fine. It’s a bit awkward, but fine. The only problem I ran into was when I realized I needed to handle non-const pointers in a correct way. There’s no easy way to identify these by just their type, since there are legitimate reasons to take a non-const pointer without modifying it.
For this, I opted for needing to manually define which functions have an “inout” type. For these, the functions need to be defined more like this:
// given void UpdateCamera(Camera *camera, int mode);
pub fn update_camera(camera: *raylib.Camera, mode: c_int) *raylib.Camera {
.UpdateCamera(camera, mode);
raylibreturn camera;
}
This isn’t too bad, and it pretty much wraps up all the caveats.
Next up
The first thing I want to do is use something other than Doxygen
and xmerl
. It’s a bit too fragile, and unpleasant to work
with. Not the fastest either, if there are a lot of files
involved.
Then I want to publish this as something you can use in a project easily.
Finally, there are some interesting things to consider in terms of how to interact with Raylib from Elixir.