2012-09-10 9 views
15

Tengo un código de C++ convertido a JavaScript mediante Emscripten. Me gustaría que el código convertido C++ vuelva a llamar al código JavaScript que lo llama. Algo así como:Pasando la función JS al código generado por Emscripten

JavaScript:

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

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

C++:

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

¿Es posible esto de alguna manera?

Respuesta

13

Creo que la respuesta aceptada está un poco desactualizada.

Consulte 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); 

De esta manera el código C no necesita ser consciente de que está transpiled a JS y los puentes necesarios puramente puede ser controlado desde JS .

(código hackeó mensaje compositor, puede contener errores)

+0

una cosa importante es que el número de punteros de función que son válidos simultáneamente es fijo y está especificado por 'emcc ... -s RESERVED_FUNCTION_POINTERS = 20 ...' –

10

Una cosa que se hace con frecuencia en Emscripten es asignar tipos fuertes a los simples.

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!"); 
} 

Y, por supuesto, se puede añadir un poco de código de unión, por lo que este se vuelve muy transparente.

+3

+1 por mencionar para evitar el cambio de nombre de C++. – Eonil

+0

puede ver esta pregunta relacionada: http://stackoverflow.com/questions/33673575/where-should-i-defined-emscripten-extern-functions-in-js?noredirect=1#comment55119884_33673575 –

1

que necesitaba escribir algo muy similar a lo que se describe en la pregunta. Mi código terminó con este aspecto:

C:

void call(void (*back)(char*)){ 
    back("Hello!"); 
} 

JS:

function back(text){ 
    alert(Pointer_stringify(text)); 
} 
var pointer = Runtime.addFunction(back); 
var call = Module.cwrap('call', 'void', ['pointer']); 
call(pointer); 
Runtime.removeFunction(pointer); 

Tenga en cuenta que el puntero devuelto a la devolución de llamada tiene que ser dereferenced con Pointer_stringify.

Puedes encontrar example code así en GitHub.

+0

El enlace proporciona casi ninguna información adicional. –

0

Esto es lo que he recogido de varios puestos y mirando emscripten código incluido:

En C++:

#include <iostream> 
#include <functional> 

extern "C" { 
    void registerCallback(void(*back)(const char*)); 
    void triggerCallback(char* message); // for invoking it from JS, just for this example 
} 

// global 
std::function<void(const char*)> gCallback; 

void registerCallback(void(*back)(const char*)){ 
    gCallback = back; 
} 

void triggerCallback(char* message){ 
    if (gCallback) { 
    gCallback(message); 
    } else { 
    std::cerr << "Cannot pass '"<< message <<"' to undefined callback\n"; 
    } 
} 

Una cosa importante, que faltaba en otras críticas, es compilar C++ con RESERVED_FUNCTION_POINTERS = ... bandera, por ejemplo:

em++ -std=c++11 -s RESERVED_FUNCTION_POINTERS=20 source.cpp -s EXPORTED_FUNCTIONS="['_registerCallback','_triggerCallback']" -o try.html 

Después try.html carga en un navegador, se puede ejecutar el siguiente código JS en su consola:

// Register a callback function 
function callback(text){ alert("In JS: "+Pointer_stringify(text)); } 
var cb = Runtime.addFunction(callback); 
_registerCallback(cb); 

// Invoke it with some "C string" 
var jsStr = "XOXOXO"; 
var cStr = allocate(intArrayFromString(jsStr), 'i8', ALLOC_NORMAL) 
_triggerCallback(cStr); 

// Free Emscripten heap and release the function pointer 
_free(cStr); 
Runtime.removeFunction(cb); 

Debería ver una alerta con "En JS: XOXOXO ".

2

Hay una nueva forma de lograr su requisito que es a través de embind.

Considere la siguiente pieza de código C++.

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

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

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

La función cbTest C++ toma en un emscripten::val. Esto puede ser un objeto de cualquier tipo. Para nosotros, este es un objeto de función. Esta es la forma en que llamaremos a partir de JS

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

Module.cbTest(cbFunc); 

P.S Esta API está todavía en construcción.

Cuestiones relacionadas