Передача JS-функции в код, созданный Emscripten

У меня есть фрагмент кода C ++, преобразованный в JavaScript с помощью Emscripten. Я хотел бы, чтобы преобразованный код C ++ перезвонил коду JavaScript, который его вызывает. Что-то типа:

JavaScript:

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

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

C++:

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

Возможно ли это как-нибудь?


person Lóránt Pintér    schedule 10.09.2012    source источник


Ответы (5)


Я считаю, что принятый ответ немного устарел.

См. этот пункт в разделе «Взаимодействие с code "учебник по emscripten.

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

Таким образом, C-код не должен знать, что он транслируется в JS, и любые необходимые мосты могут управляться исключительно из JS.

(код взломан в составителя сообщений; может содержать ошибки)

person Daniel Baulig    schedule 07.11.2013
comment
важно то, что количество одновременных действительных указателей на функции фиксировано и определяется emcc ... -s RESERVED_FUNCTION_POINTERS=20 ... - person Xavier Combelle; 22.10.2015

Существует новый способ выполнения ваших требований: встроить.

Рассмотрим следующий фрагмент кода C ++.

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

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

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

Функция cbTest C ++ принимает emscripten :: val. Это может быть объект любого типа. Для нас это функциональный объект. Вот как вы это назовете из JS

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

Module.cbTest(cbFunc);

P.S Этот api все еще находится в разработке.

person Tarun Gehlaut    schedule 07.08.2016

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

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

И, конечно же, вы можете добавить немного связующего кода, так что это будет очень плавно.

person abergmeier    schedule 10.09.2012
comment
можете ли вы посмотреть на этот связанный вопрос: stackoverflow.com/questions/33673575/ - person Chakradar Raju; 12.11.2015

Мне нужно было написать что-то очень похожее на то, что описано в вопросе. В итоге мой код выглядел так:

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

Обратите внимание, что указатель, возвращаемый обратному вызову, необходимо разыменовать с помощью Pointer_stringify.

Вы можете найти пример кода, подобный этому, на GitHub.

person Infinite Internship    schedule 11.09.2014
comment
Ссылка практически не дает дополнительной информации. - person Hristo Valkanov; 11.09.2014

Вот что я собрал из нескольких сообщений и просмотрев связанный код Emscripten:

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

Важная вещь, которой не хватало в других сообщениях, - это скомпилировать C ++ с флагом 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);

Вы должны увидеть предупреждение с надписью «In JS: XOXOXO».

person Boris    schedule 25.07.2016