Reputation: 122
I have a small question about Emscripten. How can I call C++ callback asynchronously from JavaScript?
This is my JS code:
<script type="text/javascript">
function sendRequest(callback) {
setTimeout(function(){
callback["sayHi"]();
}, 100);
}
</script>
This is my C++ code:
#include <emscripten/emscripten.h>
#include <emscripten/bind.h>
using namespace emscripten;
class MyClass {
public:
void sayHi () {
printf("Hello! \n");
};
};
EMSCRIPTEN_BINDINGS(MyClass)
{
class_<MyClass>("MyClass")
.function("sayHi", &MyClass::sayHi);
}
int main() {
val window = val::global("window");
auto myObj = MyClass();
window.call<void>("sendRequest", myObj);
return 0;
}
When I execute this code it fails with error:
Uncaught BindingError: Cannot pass deleted object as a pointer of type MyClass*
I use emcc 1.35.22 and compile it with this command:
~/app/emsdk_portable/emscripten/tag-1.35.22/emcc main.cpp --bind -o out.js
Upvotes: 1
Views: 2395
Reputation: 27012
For some reason, when you call
window.call<void>("sendRequest", myObj);
by the time the stack has cleared from the above line, Emscripten/embind deletes myObj
(You can see this if you add a destructor to MyClass).
A secondary issue is that even if Emscripten/embind didn't do this, when you do
auto myObj = MyClass();
in main
, myObj
is created on the stack, and so it would be deleted at the end of main
, which is before the asynchronous callback.
A way around both of these is to create the object on the heap, pass it to Javascript as a raw pointer, along with a function pointer to a static callback. You can use EM_ASM_ARGS
to call out from C++, and then use a dynCall_*
function to call from the function pointer from Javascript.
For example, the C++ would be like
void callback(MyClass *myObj)
{
myObj->sayHi();
}
MyClass *myObj;
int main() {
myObj = new MyClass();
EM_ASM_ARGS({
sendRequest($0, $1);
}, &callback, myObj);
// myObj is still in memory
// be sure to delete it
return 0;
}
and the Javascript
Module = {
noExitRuntime: true
};
function sendRequest(callback, myObj) {
setTimeout(function() {
Module.dynCall_vi(callback, myObj);
}, 1000);
}
Upvotes: 2