Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing JS function to Emscripten-generated code

I have a piece of C++ code converted to JavaScript via Emscripten. I would like the converted C++ code to call back to the JavaScript code that calls it. Something like:

JavaScript:

function callback(message) {
    alert(message);
}

ccall("my_c_function", ..., callback);

C++:

void my_c_function(whatever_type_t *callback) {
    callback("Hello World!");
}

Is this possible somehow?

like image 951
Lóránt Pintér Avatar asked Sep 10 '12 20:09

Lóránt Pintér


3 Answers

I believe the accepted answer is a bit outdated.

Please refer to this bullet point in the "Interacting with code" emscripten tutorial.

E.g. C:

void invoke_function_pointer(void(*f)(void)) {
  (*f)();
}

JS:

var pointer = Runtime.addFunction(function() { 
  console.log('I was called from C world!'); 
});
Module.ccall('invoke_function_pointer', 'number', ['number'], [pointer]);
Runtime.removeFunction(pointer);

This way the C-code does not need to be aware of that it is transpiled to JS and any bridges required can purely be controlled from JS.

(code hacked into message composer; may contain errors)

like image 62
Daniel Baulig Avatar answered Nov 19 '22 00:11

Daniel Baulig


There is a new way of achieving your requirement which is via embind.

Consider the following piece of C++ code.

#include <emscripten/bind.h>
using namespace emscripten;

void cbTest(emscripten::val cb)
{
    cb();
}

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

The cbTest C++ function takes in a emscripten::val. This can be an object of any kind. For us this is a function object. This is how you will call it from JS

var cbFunc = function() {
    console.log("Hi, this is a cb");
}

Module.cbTest(cbFunc);

P.S This api is still under construction.

like image 32
Tarun Gehlaut Avatar answered Nov 19 '22 00:11

Tarun Gehlaut


A thing that is frequently done in Emscripten is to map strong types to simple ones.

JS:

function callback(message) {
    alert(message);
}

var func_map = {
    0: callback
};

// C/C++ functions get a _ prefix added
function _invoke_callback(callback_id, text_ptr) {
    func_map[callback_id](Pointer_stringify(text_ptr));
}

ccall("my_c_function", ..., 0);

C++:

// In C/C++ you only need to declare the func signature and
// make sure C is used to prevent name mangling
extern "C" void invoke_callback(int callback_id, const char* text);

void my_c_function(int callback_id) {
    invoke_callback( callback_id, "Hello World!" );
}

And of course, you can add some glue code, so this gets very seamless.

like image 11
abergmeier Avatar answered Nov 19 '22 00:11

abergmeier