I’ve been experimenting with C bindings in Mojo to understand how they work. To test, I created a simple example where a C function initializes a struct, and I use it in Mojo. However, I am getting unexpected memory values and don’t understand what’s happening. Here’s my setup:
C Code (a.c):
#include <stdio.h>
typedef struct {
int x;
int y;
} Point;
Point init_point(int x, int y) {
Point p;
p.x = x;
p.y = y;
return p;
}
When I run the Mojo program, I expect the output to be (1, 2), but instead, I get: (-881658824, 32766)
I’m trying to figure out how the memory layout is being handled between Mojo and C. Could this issue be related to alignment, padding, or differences in how structs are represented? Is there something wrong with how I defined the Point struct in Mojo or how I used the _get_dylib_function? I’d really appreciate any insights into what might be going on! Thank you.
Unfortunately, the ABI of C is horrifically complicated (and extremely target specific) when it comes to passing/returning structs around by-value. Mojo does not make an attempt to know these rules. To address this, please take and return pointers, e.g. write your C code like this:
void init_point2(int x, int y, Point *result) {
result->x = x;
result->y = y;
}
If you’re interested in the archane details, check out the “Platform specific abi” (aka “PSABI”) for the architectures you’re interested in, e.g. x86-64 is here:
You can see the crazy rules in “3.2.3 Parameter Passing”. At some point, Mojo is likely to integrate with Clang so we can have really beautiful C/C++ FFI (like Swift does) but this is not on the roadmap yet.
I’ve tried using a signature with pointers before, but I’m unsure how to adapt that into Mojo. In C, you can pass uninitialized data to a function, but in Mojo, as far as I understand, you can’t pass UnsafePointer(to=self) in __init__ because self isn’t initialized at that point and you get a compiler error.
In this simple example I can fill struct with default values(or even initialize it directly with arguments, bypassing the need for ABI entirely) before calling into C, but for more complex things I think this might not be the best option. Are there any better ways to handle this kind of situation in Mojo?