Embind pass lambda function as callback parameter
Asked Answered
H

3

5

I would like to create a function, which calculates something. And after it's finished, it calls the callback function.

void calculate(int param1, ..., std::function<void(void)> callback) {
    //code...
    callback();
}

The binding of the function is created using Embind:

EMSCRIPTEN_BINDINGS(my_module) {
    function("calculate", &calculate);
}

But if I try to call Module.calculate(0, ..., function(){/*...*/}) I get this error:

UnboundTypeError: Cannot call calculate due to unbound types: NSt3__18functionIFvvEEE
Hopefully answered 2/4, 2016 at 8:49 Comment(1)
There is a thread about this on the emscripten GitHub, looks like it is possible using embind. github.com/kripken/emscripten/issues/4927Murrhine
C
5

I'm not sure if it's the only way, but to pass callbacks from Js -> C++, I've had to

  • Not use std::function, but raw function pointers
  • Use Runtime.addFunction to get a C++ function pointer for a Javascript function, and pass that into the C++ world instead of trying to pass a Javascript function directly.
  • Not use EMBIND, but rather the cwrap/ccall API.

For your example, simplifying the interface slightly so only the callback is passed from JS -> C++, the C++ could look like the following:

extern "C" {

EMSCRIPTEN_KEEPALIVE
void calculate(void (*callback)()) {
  callback();
}

}

The Javascript to create the callback function pointer could look like

var callbackPointer = Module.Runtime.addFunction(function() {
  console.log('In the callback');
});

and to call the function, passing the pointer (as a "number"):

Module.ccall('calculate', 'number', ['number'], [callbackPointer]);

and then compiling making sure to reserve room for the function pointer:

em++ app.cpp -s RESERVED_FUNCTION_POINTERS=1 -o app.js
Choric answered 4/4, 2016 at 20:53 Comment(2)
Thank you for the help. But what if my calculate function is a class method, and I would like to have more instances at a time?Hopefully
You should be able to create the C++ objects and pass raw pointers to them to Javascript. A staticcalculate function could then take, along with the function pointer of the callback, a pointer to one of these objects. It will then dereference the object pointer, and call its instance calculate method.Choric
A
4

You can now do it like this:

void calculate(int param1, ..., emscripten::val callback) {
    callback();
}

The EMSCRIPTEN_BINDINGS is unchanged.

The callback can even take parameters, provided they are of supported/declared types (e.g. if you pass an std::vector<blah> parameter to the callback, make sure you've used register_vector<blah>().)

Antiicer answered 23/11, 2017 at 9:40 Comment(1)
This is the most convenience way, I write a sample can refer sdk_web_assemblyCorallite
F
0

You can use Scapix to pass parameters like std::function, std::vector, std::map, etc.

In addition to generating bindings automatically, Scapix also adds conversion layer for (arbitrarily nested) common C++ Standard Library types.

Disclaimer: I am the author of Scapix Language Bridge.

Foul answered 28/6, 2022 at 13:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.