一尘不染

使用 emscripten 将 JS 对象传递给 C++ 的内置方法

javascript

是否有一种简单的内置方法可以将 JS 对象传递给 C++?

我尝试以明显的方式做到这一点:

回声.cpp:

#include <iostream>

#include <emscripten.h>
#include <emscripten/val.h>

using emscripten::val;

#ifdef __cplusplus
extern "C"  {
#endif

EMSCRIPTEN_KEEPALIVE void echo(val x){
    val::global("console").call<void>("log", x);
}

int main(int argc, char **argv){
    return 0;
}


#ifdef __cplusplus
}
#endif

脚本.mjs:

import initEM from "./echo.mjs";

var mod = await initEM();

export function echo(x){
  mod.ccall("echo", "void", ["object"], [x]);
}

echo({attr: 9});

编译使用:

emcc ./echo.cpp -o ./echo.mjs \
  -sEXPORTED_FUNCTIONS=_main,_echo \
  -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,registeredTypes \
  -lembind --bind

但我得到一个错误:

Uncaught TypeError: Cannot read properties of undefined (reading 'refcount')
    at __emval_incref (echo.mjs:2622:29)
    at echo.wasm:0x1957
    at echo.wasm:0x1842
    at echo.wasm:0x121c
    at echo.wasm:0x110f
    at echo.wasm:0x104a
    at echo.mjs:1639:22
    at Object.ccall (echo.mjs:845:18)
    at echo (script.mjs:6:7)
    at script.mjs:9:1

经过一些试验和错误,我得到了它的工作:

回声.cpp:

#include <iostream>

#include <emscripten.h>
#include <emscripten/val.h>

using emscripten::val;
using emscripten::internal::EM_VAL;

#ifdef __cplusplus
extern "C"  {
#endif

EMSCRIPTEN_KEEPALIVE void echo(EM_VAL x_ptr){
    // converts it to object from the pointer
    val x = val::take_ownership(x_ptr);
    val::global("console").call<void>("log", x);
}

int main(int argc, char **argv){
    return 0;
}


#ifdef __cplusplus
}
#endif

脚本.mjs:

import initEM from "./echo.mjs";

var mod = await initEM();

let objToC = null;
for(let tp of Object.values(mod.registeredTypes)){
  if(tp.name=="emscripten::val"){
    // turns it into a pointer (I think)
    objToC = (v) => tp.toWireType(null, v);
    break;
  }
}
if(objToC==null){
  throw new ReferenceError("val.toWireType not found");
}

export function echo(x){
  mod.ccall("echo", "void", ["number"], [objToC(x)]);
}

echo({attr: 9});

(使用与另一个相同的东西编译)

问题:

  1. 为什么这不在ccall/cwrap函数中?
  2. 为什么 emscripten 不公开val.toWireType作为模块对象的属性(即为什么我必须遍历所有类型才能找到它),或者我错过了什么?

阅读 141

收藏
2022-07-26

共1个答案

一尘不染

通过文档

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

my_module只是您必须添加的(全局)唯一名称,它没有任何其他用途。

void echo(EM_VAL x_ptr){
  // converts it to object from the pointer
  val x = val::take_ownership(x_ptr);
  val::global("console").call<void>("log", x);
}
EMSCRIPTEN_BINDINGS(my_bindings) {
  function("echo", echo);
}

现在你可以从 JS 调用echo而不使用ccall

Module.echo({addr: 9});

请注意,这对网络工作者来说效果不佳;echo方法的注册Module是作为 WASM 初始化的一部分完成的,并且仅在初始化线程中完成。

虽然EMSCRITEN_BINDINGS看起来很神奇,但它基本上只是制作了一个静态全局函数并在静态构造时调用它。这是

function("echo", echo);

完成所有工作;它确定 的参数echo,并构建一个 JS 包装器,用于转换参数并使用名称调用C++函数。echo``"echo"

2022-07-26