andriiheonia
andriiheonia

Reputation: 122

Call C++ function from JavaScript asynchronously using Emscripten

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

Answers (1)

Michal Charemza
Michal Charemza

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

Related Questions