COMMUNITY

[hxcpp] Proper way to pass a callback as param in to haxe, store, and call later?

haxe-cpp

(Shintiger) #1

public static function setCallback(cb:cpp.CallableBool>):Void{
storedCb = cb;
}

Interpreted to:
Main::setCallback(::cpp::Function< bool (int) > cb)…

How to cast a c++ method as ::cpp::Function< bool (int) >?


(Jérémy Faivre) #2

A trick I use when I want to call C++ from Haxe and vice versa:

When I want to create a haxe-compatible type from C++ (e.g. a callback like in your situation), I first try to create this code with haxe, and see what is the C++ output from haxe/hxcpp transpiler. Then I try to reproduce the same construct from C++ directly. For instance, if you want to know how to create a callback that gets a boolean as argument, you could try to see how a callback is created with HXCPP by checking out the C++ output of this haxe code:

class Main {
  public static function main() {
    var cb = function(arg:Bool) {
      trace('arg: ' + arg);
    };
    cb(true);
  }
}

Then you can try to reproduce the same construct on your C++ side, it should be compatible with Haxe.
In you case, you could create a callback that wraps whatever C++ function call you want.

There may be simpler solutions though, you could avoid the use of any callback on C++ side and simply provide a C++ method as haxe extern (an example of extern just in case: linc_icloud) that takes an additional handle argument (and integer ?) that allows your C++ side to retrieve any related data it needs without having to manage callback conversion from cpp to haxe.

No idea if there is a more straightforward way to cast a C++ method to :cpp::Function< bool (int) >, but I find checking out haxe -> C++ output is usually helping a lot to do what you want.


(Dmitry Hryppa) #3

You can pass regular C++ function as cpp.Callable. But you need to set a return type too.
For example, Haxe side:

public function setCallback (  cb:cpp.Callable<Bool->cpp.Void>  ):Void {
    cb.call(true);
}

Then you can pass regular C++ function on the C++ side:

void my_cpp_function (bool param) {
}

my_haxe_obj->setCallback ( my_cpp_function );

Note that C++ lambda will not work.


(Shintiger) #4

I have tried your case which generated a non-human-readable code as a closure function, and output type is Dynamic, play with Dynamic like workaround rather than proper way.
Just realized cpp.Callable may more suitable for my case.


(Shintiger) #5

I get it works with cpp.Callable now, but I want to wrap it in a cross target way.

package ;

class ExternalCallback<T> 
{
	public var callable:cpp.Callable<T>;
	public function new() {}
	public static function fromNative<T>(callback:cpp.Callable<T>):ExternalCallback<T>{
		var externalCallback:ExternalCallback<T> = new ExternalCallback<T>();
		externalCallback.callable:cpp = callback:cpp;
		return externalCallback;
	}
}

Generated:

::ExternalCallback ExternalCallback_obj::fromNative(::cpp::Function< void  (void) > callback){
...

Unfortunately, T forced to void (void), so I have to implement a specific function for cpp only when dealing with different args structure?


(Dmitry Hryppa) #6

You forget about a return type for your Callable class.
Try to use cpp.Callable<T->cpp.Void>

Also, T is not available in generated code because it is generic class and it will be forced as Dynamic or something like that.
But you can try to use @:generic meta to avoid this.


I’m not sure, how you will make it in cross way. Because some targets has pretty different ways to make callbacks. In Java it is an interfaces, for example. So, you maybe need something like that:

#if cpp
typedef Callback = cpp.Callable<()->cpp.Void>;
#elseif java
interface Callback {
    public function call():Void;
}
#else
...and so on 
#end

But maybe you will find a better way. :slight_smile:


(Shintiger) #7

I think so, thanks for sharing.


(Jérémy Faivre) #8

Right, casting to/from cpp.Callable seems to be a better approach in your situation anyway, as suggested by @dmitryhryppa .

As a side note I would not worry too much about Dynamic being used in place of callback types on haxe/hxcpp side though, it seems that it’s what HXCPP uses on callback types transpiled from Haxe code (as you have noticed when checking the non-human readable c++ output), and it doesn’t seem to be an issue in general as you can use yourCallback->__run(arg1, arg2, ...) to call the underlying function hold by your Dynamic object. That’s exactly what generated Haxe/C++ code does anyway when you call a callback from haxe code. If it’s the default way of calling callbacks in hxcpp, I would expect it is fairly well optimized. However, you are indeed loosing type safety on C++ side in that case, so using cpp.Callable is still a better option probably!