将JS函数传递给Emscripten生成的代码

21

我有一段通过 Emscripten 转换成 JavaScript 的 C++ 代码。我希望转换后的 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!");
}

这是否有可能以某种方式实现?
5个回答

17

有一种新方法可以实现您的要求,即通过embind

考虑以下这段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);

附言:此API仍在建设中。


17
我认为接受的答案有点过时了。
请参考“与代码交互”emscripten教程中的此项目符号
例如: C:
void invoke_function_pointer(void(*f)(void)) {
  (*f)();
}

JS:

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控制。
(代码被黑客破解到消息合成器中;可能包含错误)

一个重要的事情是函数指针的数量是固定的,并且由 emcc ... -s RESERVED_FUNCTION_POINTERS=20 ... 指定。 - Xavier Combelle

11
在 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!" );
}
当然,你可以添加一些粘合代码,使其变得非常无缝。

你能看一下这个相关的问题吗:http://stackoverflow.com/questions/33673575/where-should-i-defined-emscripten-extern-functions-in-js?noredirect=1#comment55119884_33673575 - Chakradar Raju

1
以下是我从几篇文章和查看 Emscripten 捆绑代码中收集到的信息:
在 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";
  }
}

在其他文章中缺少的一件重要事情是使用RESERVED_FUNCTION_POINTERS=...标志编译C++,例如:

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" 的警告框。

1
我需要编写与问题描述类似的内容。我的代码最终看起来像这样:

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 上找到类似 示例代码 的内容。

该链接几乎没有提供任何额外的信息。 - Hristo Valkanov
1
请使用UTF8ToString替代Pointer_stringify,因为后者已被移除。 - marczellm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接