4

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?

AlexRamallo
  • 637
  • 1
  • 5
  • 23

1 Answers1

5

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.

Method 1

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

Method 2

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++.

Disclaimer

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. ‍♂️

AlexRamallo
  • 637
  • 1
  • 5
  • 23