1

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
2
  • Does it work if the code is not asynchronous? Commented Mar 4, 2016 at 21:20
  • Yes if I call callback["sayHi"](); before setTimeout then it works. Commented Mar 4, 2016 at 23:02

1 Answer 1

2

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);
}
Sign up to request clarification or add additional context in comments.

3 Comments

Hi Michal, Thank you for your answer! But in a real world application my "sendRequest" is not a global function. Can I somehow call method of the JS object (method of the emscripten::val) inside EM_ASM_ARGS block? Something like this?
@AndriiHeonia There is at least one way of calling non-global JavaScript functions from C++. I'm not sure if it's applicable to your case, so my suspicion is that it's probably better to post another question with the specifics, than to try to get it sorted in comments.
I created new question: stackoverflow.com/questions/35846606/… And I would be happy to read your ideas :) Thanks in advance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.