haxeopenflhxcpp

Call method in object passed to C++ from Haxe


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?


Solution

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