I'm creating an OpenFL extension for a mobile advertising SDK, and I'm having difficulty figuring out some CFFI stuff.
Basically, I'm trying to pass a Haxe object to C++, and then call a method on that object from C++. The purpose of this is as an event listener, so when something happens in C++, I will call a callback on that object to notify the Haxe code.
I know how to do this with Java using lime's JNI stuff for Android. That looks something like this using JNI type signatures:
var setCallbackListener = JNI.createStaticMethod("com.test.myextension", "setCallbackListener", "(Lorg/haxe/lime/HaxeObject;)V");
var listener = new MyCallbackListener(); //implements `onSomething`
setCallbackListener(listener); //pass the listener to the Java side
Then from the Java side, I can call the function onSomething
like this:
public static void setCallbackListener(HaxeObject listener){
listener.call0("onSomething"); //call a function called "onSomething" with zero arguments
}
And that works, and it's how I'm doing it for Android. For iOS, I'm trying to do the same thing, but with hxcpp.
I know the general process for calling a C++ function from Haxe, using cpp.Lib.load
in a similar way to the JNI api above. However, once I get a value
type on the C++ side, I don't know how I can call a member function on it.
for example, say my C++ function looks like this:
#include <hx/CFFI.h>
static void setCallbackListener (value listener) {
//...
}
DEFINE_PRIM (setCallbackListener, 1);
How would I then call the function "onSomething" in listener
?
After some digging I figured it out! Unfortunately, none of this seems to be documented at all, so I figured I'd post my findings here in case someone else wants to do the same thing.
The hxcpp C API is apparently very similar to the Neko one, although not exactly the same. Still, the Neko documentation is a good starting point.
That page shows you how to call a function by using val_call0
, val_call1
, etc. What it doesn't show you is how to obtain a reference to the function you want from an instance. To do that, you can use the functions val_id
and val_field
together like this (this is for hxcpp, not sure if it also works for Neko):
#include <hx/CFFI.h>
#include <hx/CFFIAPI.h>
static void setCallbackListener (value listener) {
//get the field "id" from the name, which seems like a generic hash
field fid = val_id("onSomething");
//get the function from `listener`
value myfunc = val_field(
listener, //class instance allocated in haxe (type `value`)
fid
);
//optionally verify that `myfunc` is 1) a function and 2) accepts 0 arguments
//throws haxe error if this check fails
val_check_function(myfunc, 0);
//perform the call to `listener.onSomething()`
val_call0(myfunc);
}
DEFINE_PRIM (setCallbackListener, 1);
If the function took 2 arguments instead of one (for example), then you'd use val_call2
, and call it like this:
value arg1 = alloc_string("foo");
value arg2 = alloc_bool(true);
val_call2(myfunc, arg1, arg2); //arguments are of type `value`
There is also val_callN
which accepts an arbitrary amount of arguments. See the declaration here.
Reference: https://github.com/HaxeFoundation/hxcpp/blob/master/project/libs/std/Socket.cpp#L1039
Fortunately, there were only 3 results when I Googled "hxcpp val_call" (without quotes), and one of those links detailed this method.
Just read through that thread for a full explanation. That method would probably be preferred if you need better memory management, as it also shows how to tie in to the hxcpp GC from your C++.
I couldn't find any actual documentation on val_id
and val_field
, so it's possible that these methods aren't meant to be used externally and might change in a later version of hxcpp. I have no idea. 🤷♂️