2012-09-10 17 views
15

Ich habe ein Stück C++ - Code in JavaScript über Emscripten konvertiert. Ich möchte, dass der konvertierte C++ - Code den JavaScript-Code aufruft, der ihn aufruft. Etwas wie:JS-Funktion an Emscript-generierten Code übergeben

JavaScript:

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

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

C++:

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

Ist das irgendwie möglich?

Antwort

13

Ich glaube, die akzeptierte Antwort ist ein bisschen veraltet.

Bitte beziehen Sie sich auf this bullet point in the "Interacting with code" emscripten tutorial.

z. 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); 

Auf diese Weise der C-Code nicht bewusst sein muss von, dass es zu JS und alle erforderlichen Brücken transpiled ist, kann rein von JS gesteuert werden .

(Code in Nachrichtenersten gehackt; Fehler enthalten kann)

+0

eine wichtige Sache ist, dass Die Anzahl der gleichzeitig gültigen Funktionszeiger ist fest und wird durch 'emcc ... -s RESERVED_FUNCTION_POINTERS = 20 ...' –

10

Eine Sache, die in Emscripten häufig gemacht wird, ist, starke Typen zu einfachen zu mappen.

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

Und natürlich können Sie etwas Klebstoff-Code hinzufügen, so dass dies wird sehr nahtlos.

+3

+1 für die Erwähnung zu vermeiden C++ Name Mangling. – Eonil

+0

können Sie diese verwandte Frage betrachten: http://stackoverflow.com/questions/33673575/where-should-i-defined-emscripten-extern-functions-in-js?noredirect=1#comment55119884_33673575 –

1

Ich brauchte etwas sehr ähnlich zu schreiben, was in der Frage beschrieben. Mein Code endete wie folgt aussehen:

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

Beachten Sie, dass der Zeiger auf den Rückruf zurückgegeben hat mit Pointer_stringify werden dereferenziert.

Sie können example code so auf GitHub finden.

+0

angegeben. Der Link bietet nahezu keine zusätzlichen Informationen. –

0

Hier ist, was ich von mehreren Stellen und durch einen Blick auf Emscripten gebündelten Code gesammelt habe:

In 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"; 
    } 
} 

Eine wichtige Sache, die in anderen Beiträgen fehlt, ist C++ zu kompilieren mit RESERVED_FUNCTION_POINTERS = ... Flagge, zB:

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

Nach dem Laden try.html in einen Browser, können Sie den folgenden JS-Code in seine Konsole ausführen:

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

Sie sollten eine Warnung mit „In JS sehen: XOXOXO ".

2

Es gibt eine neue Möglichkeit, Ihre Anforderung zu erfüllen, nämlich embind.

Betrachten Sie das folgende Stück C++ - Code.

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

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

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

Die cbTest C++ Funktion nimmt in einem emscripten::val. Dies kann ein Objekt jeglicher Art sein. Für uns ist das ein Funktionsobjekt. Dies ist, wie Sie es von JS

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

Module.cbTest(cbFunc); 

P. S Dieses api noch ist.