Передача JS-функции в код, созданный Emscripten
У меня есть код С++, преобразованный в JavaScript через Emscripten. Я бы хотел, чтобы преобразованный код С++ перезвонил ко второму JavaScript-коду, который его вызывает. Что-то вроде:
JavaScript:
function callback(message) {
alert(message);
}
ccall("my_c_function", ..., callback);
С++:
void my_c_function(whatever_type_t *callback) {
callback("Hello World!");
}
Возможно ли это как-то?
Ответы
Ответ 1
Я считаю, что принятый ответ немного устарел.
Пожалуйста, обратитесь к этой маркерной точке в учебнике emscripten "Взаимодействие с кодом" .
например.
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);
Таким образом, C-код не должен знать, что он переводится в JS, и любые требуемые мосты могут управляться исключительно из JS.
(код взломан в композитор сообщений, может содержать ошибки)
Ответ 2
То, что часто делается в Emscripten, - это сопоставление сильных типов с простыми.
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);
С++:
// 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!" );
}
И, конечно же, вы можете добавить некоторый код клея, так что это становится очень плавным.
Ответ 3
Существует новый способ достижения вашего требования: embind.
Рассмотрим следующий фрагмент кода С++.
#include <emscripten/bind.h>
using namespace emscripten;
void cbTest(emscripten::val cb)
{
cb();
}
EMSCRIPTEN_BINDINGS(my_module) {
function("cbTest", &cbTest);
}
Функция cbTest С++ принимает emscripten::val. Это может быть объектом любого рода. Для нас это объект функции.
Так вы назовете это из JS
var cbFunc = function() {
console.log("Hi, this is a cb");
}
Module.cbTest(cbFunc);
P.S Этот api все еще находится в разработке.
Ответ 4
Мне нужно было написать что-то очень похожее на то, что описано в вопросе. Мой код выглядел так:
С:
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);
Обратите внимание, что указатель, возвращаемый обратному вызову, должен быть разыменован с помощью Pointer_stringify.
Вы можете найти пример кода, как это на GitHub.
Ответ 5
Вот что я собрал из нескольких сообщений и посмотрел на Emscripten в комплекте:
В С++:
#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";
}
}
Важная вещь, отсутствовавшая в других сообщениях, заключается в компиляции С++ с флагом RESERVED_FUNCTION_POINTERS =..., например:
em++ -std=c++11 -s RESERVED_FUNCTION_POINTERS=20 source.cpp -s EXPORTED_FUNCTIONS="['_registerCallback','_triggerCallback']" -o try.html
После загрузки try.html в браузер вы можете выполнить следующий JS-код в своей консоли:
// 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);
Вы должны увидеть предупреждение с "В JS: XOXOXO".