2012-09-10 6 views
15

Mam fragment kodu C++ przekonwertowany do JavaScript przez Emscripten. Chciałbym, aby przekonwertowany kod C++ oddzwonił do kodu JavaScript, który go wywołuje. Coś jak:Przekazywanie funkcji JS do wygenerowanego przez Emscripten kodu

JavaScript:

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

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

C++:

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

Czy jest to możliwe w jakiś sposób?

Odpowiedz

13

Uważam, że zaakceptowana odpowiedź jest nieco nieaktualna.

Proszę odnieść się do 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); 

ten sposób C-kod nie trzeba mieć świadomość, że jest transpiled do JS oraz wszelkie mostki wymagane może być czysto sterowane z JS .

(kod włamał wiadomości kompozytora, mogą zawierać błędy)

+0

ważne jest to, że liczba wskaźników funkcji, które są jednocześnie poprawne, jest stała i określona przez 'emcc ... -s RESERVED_FUNCTION_POINTERS = 20 ...' –

10

To, co często robi się w Emscripten, to mapowanie typów silnych na proste.

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

I oczywiście, można dodać trochę kodu kleju, więc staje się bardzo płynnie.

+3

+1 za wzmiankę o uniknięciu wymazywania nazw w C++. – Eonil

+0

Czy możesz spojrzeć na to powiązane pytanie: http://stackoverflow.com/questions/33673575/where-should-i-defined-emscripten-extern-functions-in-js?noredirect=1#comment55119884_33673575 –

1

Musiałem napisać coś bardzo podobnego do tego, co zostało opisane w pytaniu. Mój kod skończyło się patrząc tak:

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

Należy zauważyć, że wskaźnik wrócił do zwrotnego musi być dereferencjonowane z Pointer_stringify.

Możesz znaleźć example code w ten sposób na GitHub.

+0

Łącze nie zawiera żadnych dodatkowych informacji. –

0

Oto co mam zebrane z kilku stanowisk i patrząc na emscripten dołączony kod:

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

Ważną rzeczą, której brakowało w innych postach, jest do kompilacji C++ z RESERVED_FUNCTION_POINTERS = ... flaga, np:

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

Po załadunku try.html w przeglądarce, można wykonać następujący kod JS w swojej konsoli:

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

powinien pojawić się alert „w JS: XOXOXO ".

2

Istnieje nowy sposób spełnienia wymagań, który jest przez embind.

Rozważmy następujący fragment kodu C++.

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

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

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

Funkcja cbTest C++ pobiera emscripten::val. Może to być dowolny przedmiot. Dla nas jest to obiekt funkcji. ten sposób można wywołać ją z JS

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

Module.cbTest(cbFunc); 

P.S Ten interfejs API jest jeszcze w budowie.