joy of programming
I hadn't done any low level programming for a while before I started using Odin. Now I can't get enough.
Odin, while great, does have its rough edges. You can expect that from a newer language with a smaller community. And because the creator [Ginger Bill](https://twitter.com/TheGingerBill) develops on a Windows machine there's a few more rough edges in MacOS vs Windows. I am going to talk about one of the issues I ran into on Mac and how I solved it.
(Look I develop on MacOS. I come from a web background and like the OS and it's ecosystem. Sue me.)
OSX Calling Convention Bug
Related Issue
I am not sure of the root problem, but Odin isn't passing certain structs properly. I think it has to do with the MacOS ABI wanting them passed as a 128 bit vector, but again I'm not sure.
This appears to be a major issue and it is, however we can work around it.
In my code and tool I use Raylib []. Odin's foreign system is awesome, making it super easy to include anything compiled to C code. I link to the raylib .a file and done... In a perfect world. As mentioned, the calling convention is broken for a few structs. But as mentioned, there is a solution. It's a simple solution. We can pass in our structs normally and modify our calls to pass in a pointer parameter for output instead of directly returning.
1 | RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Returns the screen space position for a 2d camera world space position |
Becomes
1 | RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera, Vector2 *out); // Returns the screen space position for a 2d camera world space position |
The above method I was doing this a not smart way. I was modifying the entire raylib source to make these changes to the calls. This worked fine. I didn't think much of it as I was building out Cute Exporter. Then it came time to start making sure it worked for Windows. Windows doesn't have these calling convention issues. Do I now need to maintain two different versions of raylib for MacOS and Windows solely to work around Odin? That could work but doesn't seem optimal. Then I realized, Odin has a great foreign system we can do this better.
Instead of modifying raylib directly I made a small wrapper C library around functions that needed to change. I don't even need to do it for every raylib function, only the broken ones.
And in Windows we can import the raylib library code and be done.
In the C wrapper
1 2 3 4 5 6 7 8 | #include "raylib.h" Vector2 raylibext_GetScreenToWorld2D(Vector2 vec, Camera2D cam, Vector2 *input, Vector2 *result) { Vector2 res = GetScreenToWorld2D(*input, cam); *result = res; return res; } |
I can then import my wrapper functions and work with them to have the same Odin calling convention in Windows or MacOS. (Odin also has a great system for OS specific code.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //import_darwin.odin //The _darwin part means this file only gets included and compiled when on a Darwin (MacOS) system. when ODIN_OS == "darwin"{ foreign import raylib_host { "libraylib.a", "raylib_extended.o", } }; foreign raylib_host { @(link_name="raylibext_GetScreenToWorld2D") _GetScreenToWorld2D :: proc(raylib._Vector2, raylib.Camera2D, ^raylib._Vector2, ^raylib._Vector2) -> raylib._Vector2 ---; } GetScreenToWorld2D :: proc "c" (coord: raylib._Vector2, camera: raylib.Camera2D) -> raylib._Vector2 { result : raylib._Vector2; input := raylib._Vector2{coord.x, coord.y}; _GetScreenToWorld2D(input, camera, &input, &result); return raylib._Vector2{result.x, result.y}; } |
The advantage of this method being, I don't need to maintain a different branch of my changes to raylib. If a newer version or bug fix come out I don't need to worry about conflicts.
In the end it's nothing too complex or crazy and that is fully owed to how flexible and awesome Odin is.