javascript - asm.js Module.ccall/Module.cwrap 回调

标签 javascript c++ emscripten asm.js

我在 c++ 中有一些回调函数,我想在使用 emscripten 编译后在 javascript 中重新创建。

有谁知道如何使用 ccall 或 cwrap 调用它们?

谢谢!

最佳答案

我使用的技术是将指针转换为 unsigned int(技术上是 uint32_t),然后将其传递给 JS。当我准备好执行回调时,我将值传回 C++,将其转换回函数指针,然后调用相关函数。

其中很多都可以自动化;例如,您可以将回调类设置为具有一个基类,在调用重定向到正确派生类的虚函数之前,所有指针都将转换为该基类(在我的例子中,我使用它来处理参数和返回类型)。

我很乐意帮助您编写一些代码。我正在开展一个名为 Empirical 的项目,该项目将是一个仅包含 header 的库,主要用于将科学软件移植到网络上。它仍处于活跃的早期开发阶段,但您可能会发现一些有用的部分。

https://github.com/mercere99/Empirical

emp::JSWrap() 是一个接受 std::function 对象并返回 uint32_t 的函数。你可以在这里找到它的定义:https://github.com/mercere99/Empirical/blob/master/emtools/JSWrap.h它应该正确处理基本参数和返回类型以及 std::string,但我正在努力扩展它。如果您只是从项目中获取一些文件,请注意 JSWrap 确实包含一些其他文件,但不会太多。

另一个你需要担心的相关文件是library_emp.js(https://github.com/mercere99/Empirical/blob/master/emtools/library_emp.js),它定义了emp.Callback(),可以在JS端使用。

要将这两个文件集成到您的程序中,您需要:

  • 添加到您的 C++ 代码 #include "JSWrap.h"#include "init.h"(可能带有附加路径信息)
  • 添加到您的编译:--js-library ../../emtools/library_emp.js
  • 在使用任何回调之前,在 C++ 端运行 emp::Initialize();
  • 通过调用 uint32_t fun_id = emp::JSWrap(FunctionToBeWrapped); 包装您正在使用的函数; 它将返回一个您可以传递给 JS 的函数 ID 值。或者,您可以调用 uint32_t fun_id = emp::JSWrap(FunctionToBeWrapped, "JS_Function_Name"); 它将为您创建一个具有指定名称的 JS 函数。
  • 在 JS 中,您可以通过使用 emp.Callback(id, parameters) 触发回调来提供指定的函数 ID - 或者 - 如果您使用一个 ,则可以使用提供的名称emp.JS_Function_Name(parameters...) 它会回调原来的函数。任何返回值都将被传回。

如果这有帮助,请告诉我! JSWrap.h 顶部还有一些文档,测试文件位于 https://github.com/mercere99/Empirical/tree/master/UTests/emtools。 (包括一个 Makefile、一个名为 JSWrap.cc 的代码文件和一个名为 JSWrap.html 的 HTML 文件)。

编辑:下面是将函数对象的指针发送到 JS,然后从 JS 回调它的示例代码。

#include <emscripten.h>
#include <functional>

// A couple of possible callbacks, all with the same signature.
double Times2(double val) { return val * 2; }
double Plus7(double val) { return val + 7; }

// A function callback from JS that takes a callback id and an arg and does the callback.
extern "C" {
  double Callback_dd(uint32_t cb_id, double val) {
    auto * fun_ptr = reinterpret_cast<std::function<double(double)>*>(cb_id);
    return (*fun_ptr)(val);
  }
}

int main() {
  // Pick the function you want to run
  auto fun = std::function<double(double)>(Times2);
  // auto fun = std::function<double(double)>(Plus7);

  // Convert a function pointer to a uint32_t.
  // Note double casting to first convert it to a number and then reduce it to 32-bits.
  // Using reintepret_cast would be better, but looked confusing with the double cast.
  uint32_t cb_id = (uint32_t) (long long) &fun;

  // The following code passed the callback ID to JavaScript.  The JS code then uses the
  // ID to call back the original function.
  EM_ASM_ARGS({
      Callback_dd = Module.cwrap('Callback_dd', 'number', ['number']);
      var x = 12.5;
      alert('Result: fun(' + x + ') = ' + Callback_dd($0, x));
    }, cb_id);

}

请注意,如果您打算在 main() 结束后执行回调,则需要确保您正在调用的函数将持续存在。

要编译此代码,请将其放入一个文件(我们称之为 callback_test.cc),然后从命令行运行:

em++ -s EXPORTED_FUNCTIONS="['_Callback_dd', '_main']" -std=c++11 callback_test.cc -o callback_test.html

您现在应该能够在您的网络浏览器中打开 callback_test.html,它会从 JS 调用您传递给它的任何 C++ 函数指针。

在这种情况下,您需要提前知道函数签名,但正如我上面提到的,您可以使用可以记住签名的更精细的回调。

关于javascript - asm.js Module.ccall/Module.cwrap 回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31584109/

相关文章:

javascript - 神秘的空值添加到我的数组的开头

javascript - 使用 jquery 选择单个下一个兄弟

c++ - 模板参数列表太少问题

javascript - 未使用异步定义回调

javascript - 将 30 分钟添加到 Date 会导致它返回 30 分钟

c++ - 为什么不自动假定#pragma 一次?

c++ - 使用 C/C++ 创建多线程应用程序的最简单方法是什么?

c - 是否有可能将 exe 编译为 javascript?

c++ - 当我在调用project()之前设置(CMAKE_EXECUTABLE_SUFFIX)时,CMake中的Emscripten不会输出html文件。为什么?我该如何修复它?

llvm - 如何生成供 emscripten 使用的 LLVM 位码?