使用Emscripten将JavaScript数组字符串传递给C函数

Bra*_*non 6 javascript c emscripten webassembly

我是WebAssembly和Emscripten的新手,我正在尝试将JavaScript中的字符串数组传递给C函数,以便进一步处理Module.cwrap(...).理想情况下,我还想从C返回一个字符串数组回JavaScript.

下面是我正在寻找的一些伪代码:

JS

const strings = ["foo", "bar", "fool", "gnar"]
const result = Module.cwrap("myCFunc", "array", ["array"])
console.log(result) // ["my", "transformed", "array"]
Run Code Online (Sandbox Code Playgroud)

C

char **myCFunc(char **input) {
    // do some processing. Specifically some md5 hashing...
    return output;
}
Run Code Online (Sandbox Code Playgroud)

我的猜测是我无法从JS到C函数自己传递多维数组,但是必须使用WebAssembly堆内存.我知道emscripten JS API支持这种类型的东西,但我还没有编写C语言,这种类型的指针操作的细节目前还超出了我的范围.

mwa*_*wag 3

这里有两个问题:一个是如何将数据数组传递给 C 函数,另一个是如何从 C 函数取回数据数组。

对于第一个问题,您需要将每个 JS 字符串转换为 C 字符串,为保存每个此类 C 字符串的数组分配 C 内存,并将值写入数组 - 然后您可以将其传递给您的函数。

例如:

emcc 标志:

-s EXPORTED_FUNCTIONS="['UTF8ToString','stringToUTF8Array','lengthBytesUTF8']"

-s EXTRA_EXPORTED_RUNTIME_METHODS="['_free','_malloc','setValue']"
Run Code Online (Sandbox Code Playgroud)

JavaScript:

// convert a Javascript string to a C string
function str2C(s) {
  var size = lengthBytesUTF8(s) + 1;
  var ret = _malloc(size);
  stringToUTF8Array(s, HEAP8, ret, size);
  return ret;
}

function run_with_strings(strings) {
  let c_strings = strings.map(x => str2C(x));


  // allocate and populate the array. adapted from /sf/answers/1674192411/
  let c_arr = _malloc(c_strings.length*4); // 4-bytes per pointer
  c_strings.forEach(function(x, i) {
    Module.setValue(argsC + i * 4, x, "i32");
  });

  // invoke our C function
  let rc = myCFunc(c_strings.length, c_arr);

  // free c_strings
  for(let i = 0; i < c_strings.length; i++)
    _free(c_strings[i]);

  // free c_arr
  _free(c_arr);

  // return
  return rc;
}
Run Code Online (Sandbox Code Playgroud)

对于第二个问题,您的 C 函数可能很难使用,因为它不返回项目数(尽管我认为您可以推断如果最后一个项目为 NULL 并且没有其他项目为 NULL)。一种解决方案是让您的函数返回一个不透明的指针,然后可以将其反馈到一些辅助函数中,例如:

在C中:

struct myCFuncReturnValue {
  char **values;
  unsigned int count;
};

struct myCFuncReturnValue *myCFunc(char **input) {
  struct myCFuncReturnValue *p = calloc(1, sizeof(*p));
  ...
  p->count = ...;
  p->values = ...;
  return p;
}

char *myCFunc_value(struct myCFuncReturnValue *p, unsigned int i) {
  return i < p->count ? p->values[i] : NULL;
}

unsigned int myCFunc_count(struct myCFuncReturnValue *p) {
  return p->count;
}

void myCFunc_destroy(struct myCFuncReturnValue *p) {
  // maybe here you need to free each p->values[i];
  free(p->values);
  free(p);
}
Run Code Online (Sandbox Code Playgroud)

JavaScript:

// continuing with the rc value from run_with_strings():

for(let i = 0, j = myCFunc_count(rc); i < j; i++) {
  let c_string = myCFunc_value(rc, i);
  console.log(UTF8ToString(c_string));
}

myCFunc_destroy(rc);
Run Code Online (Sandbox Code Playgroud)