【问题标题】:How to handle passing/returning array pointers to emscripten compiled code?如何处理传递/返回数组指针到 emscripten 编译代码?
【发布时间】:2013-07-26 20:49:43
【问题描述】:

我是 Emscripten/javascript 以及这个 Overstack 社区的新手。如果我的情况已经得到解决,我提前道歉。

在 Windows 7 环境中,我使用 emcc 编译了一个简单的 c 程序,该程序接受一个数组并对其进行修改(见下文)。

double* displayArray(double *doubleVector) {

   for (int cnt = 0; cnt < 3; cnt++) 
       printf("doubleVector[%d] = %f\n", cnt, doubleVector[cnt]);

   doubleVector[0] = 98;
   doubleVector[1] = 99;
   doubleVector[2] = 100;

   for (int cnt1 = 0; cnt1 < 3; cnt1++) 
       printf("modified doubleVector[%d] = %f\n", cnt1, doubleVector[cnt1]);

   return doubleVector;
}

int main() {

   double d1, d2, d3;
   double array1[3];
   double *array2;

   array1[0] = 1.00000;
   array1[1] = 2.000000;
   array1[2] = 3.000000;

   array2 = displayArray(array1);

   for (int cntr =0; cntr < 3; cntr++)
       printf("array1[%d] = %f\n", cntr, array1[cntr]);

   for (int cnt = 0; cnt < 3; cnt++)
       printf("array2[%d] = %f\n", cnt, array2[cnt]);

   return 1;
}

使用 emcc 的 -o 选项,我生成了一个加载到浏览器 (Chrome) 的 .html 文件。

python emcc displayArray7.c -o displayArray7.html -s EXPORTED_FUNCTIONS="['_main', '_displayArray'

加载后,我看到在浏览器窗口中生成的输出符合预期(见下文)。

doubleVector[0] = 1.000000
doubleVector[1] = 2.000000
doubleVector[2] = 3.000000
modified doubleVector[0] = 98.000000
modified doubleVector[1] = 99.000000
modified doubleVector[2] = 100.000000
array1[0] = 98.000000
array1[1] = 99.000000
array1[2] = 100.000000
array2[0] = 98.000000
array2[1] = 99.000000
array2[2] = 100.000000

但是,当通过 javascript 控制台使用 module.cwrap() 命令并尝试直接调用该函数时(在 main() 之外),

> displayArray=Module.cwrap('displayArray', '[number]', ['[number]'])

> result = displayArray([1.0,2.0,3.0])
[1, 2, 3]
> result
[1, 2, 3]

我看到浏览器中正在生成/显示以下内容,这不是我期望看到的。

doubleVector[0] = 0.000000
doubleVector[1] = 0.000000
doubleVector[2] = 0.000000
modified doubleVector[0] = 100.000000
modified doubleVector[1] = 100.000000
modified doubleVector[2] = 100.000000   

我有以下问题:

  1. 在调用 Module.cwrap() 时,返回类型和参数列表的语法是否正确?我已经在教程的“与代码交互”部分成功运行了简单、直接的 int_sqrt() 示例,该示例处理将非指针变量传递给 int_sqrt() 例程。

  2. 当数组和/或指针传递给 emscripten 生成的 javascript 代码(或从其返回)时,是否会发生不同的情况?

  3. 从 main() 调用时,函数 displayArray() 在浏览器中生成的输出如何工作(如预期);但不是通过 javascript 控制台?

我是 Emscripten/javascript 的新手,因此我们将不胜感激任何信息/帮助。

谢谢,

FC

【问题讨论】:

  • 我在 Angular 8 中遇到了同样的问题。我尝试使用 WebAssembly 模块导入 WASM 文件。但我不确定您如何获得“模块”对象。对我来说,它说未定义。

标签: javascript emscripten


【解决方案1】:

Module.cwrap 的预期格式确实允许将 'array' 传递给函数,但如果您尝试返回数组,则会断言结果并失败

displayArrayA=Module.cwrap('displayArray','array',['array'])
displayArrayA([1,2,3]) 
// Assertion failed: ccallFunc, fromC assert(type != 'array')

第二个限制是传入的数组应该是字节数组,这意味着您需要将任何传入的双精度数组转换为无符号的 8 位数字

displayArrayA=Module.cwrap('displayArray','number',['array'])
displayArrayA(new Uint8Array(new Float64Array([1,2,3]).buffer))

以这种方式调用该方法将调用您的函数,将您的数组临时复制到 Emscripten 堆栈中,该堆栈将在您调用的函数执行后重置,从而使返回的数组偏移量在释放堆栈空间时可能无法使用。

如果您想要函数的结果,最好在 Emscriptens Heap 系统中分配和保存一个数组。

Emscripten 代码只能访问已在 Emscripten 堆空间中分配的内存。您尝试传递给函数的数组被分配在 Emscripten 代码正在运行的堆之外,并且与传入参数中预期的原始指针类型不匹配。

您可以通过多种方式访问​​数组以将数据传递给您的函数。所有这些都需要 Emscripen 知道您的内存在 emscripten Module.HEAP* 中的位置,因此初始步骤是在某个时候调用 Emscripten“_malloc”函数。

var offset = Module._malloc(24)

这将允许您在 Emscripten 堆中为您的 3x 8 字节双精度数组分配所需的 24 个字节,并在 Emscripten 堆中返回一个数字偏移量,表示为您的数组保留的 U8 TypedArray 偏移量。这个偏移量是你的指针,当它被配置为使用原始指针偏移量时,它会自动传递给你的 cwrap displayArray 函数。

displayArray=Module.cwrap('displayArray','number',['number'])

此时,如果你想访问或修改数组的内容,只要malloc有效,你至少有以下几种选择:

  1. 使用临时包装的Float64数组设置内存,除了以下2种访问方法外,没有简单的方法可以恢复值

    Module.HEAPF64.set(new Float64Array([1,2,3]), offset/8);
    displayArray(offset);
    
  2. Module.setValue 将使用 'double' 提示自动修改 HEAPF64 偏移量,除以 8。

    Module.setValue(offset, 1, 'double')
    Module.setValue(offset+8, 2, 'double')
    Module.setValue(offset+16, 3, 'double')
    displayArray(offset)
    var result = [];
    result[0] = Module.getValue(offset,'double'); //98
    result[1] = Module.getValue(offset+8,'double') //99
    result[2] = Module.getValue(offset+16,'double') //100
    
  3. 如果您希望在 Javascript 端更广泛地使用您的指针,您可以手动从 HEAPF64 条目中拉出一个子数组 TypedArray。这使您可以在完成函数执行后轻松读取值。这个 TypedArray 与 Emscripten 的其余部分由同一个堆支持,因此在 Javascript 端执行的所有更改都将反映在 Emscripten 端,反之亦然:

    var doublePtr = Module.HEAPF64.subarray(offset/8, offset/8 + 3);
    doublePtr[0] = 1;
    doublePtr[1] = 2;
    doublePtr[2] = 3;
    // Although we have access directly to the HEAPF64 of the pointer,
    // we still refer to it by the pointer's byte offset when calling the function
    displayArray(offset);
    //doublePtr[] now contains the 98,99,100 values
    

【讨论】:

  • 非常有帮助的答案。为我节省了数小时的拉头发时间。谢谢。
  • 确实很有帮助,但是“反过来”呢?如何从 C 代码修改数组并在 Javascript 中使用它?
  • 我看到 Module.malloc() 但没有看到 Module.free() ?是由 JS 的 GC 处理的吗?
  • @Nikkolasg 不,你必须打电话给Module._free(...)
  • 为什么要将偏移量除以8?偏移量不是已经指向正确的位置了吗?
【解决方案2】:

作为另一个答案的奖励,这里有一个用于分配 float64 数组的便利函数

function cArray(size) {
    var offset = Module._malloc(size * 8);
    Module.HEAPF64.set(new Float64Array(size), offset / 8);
    return {
        "data": Module.HEAPF64.subarray(offset / 8, offset / 8 + size),
        "offset": offset
    }
}
var myArray = cArray(3) // {data: Float64Array(3), offset: 5247688}


var displayArray = Module.cwrap('displayArray','number',['number'])
displayArray(myArray.offset)

给出原始函数的输出:

doubleVector[0] = 98.000000
doubleVector[1] = 99.000000
doubleVector[2] = 100.000000
modified doubleVector[0] = 98.000000
doubleVector[1] = 99.000000
modified doubleVector[2] = 100.000000

【讨论】:

    猜你喜欢
    • 2018-04-07
    • 1970-01-01
    • 2019-11-07
    • 1970-01-01
    • 2018-09-23
    • 1970-01-01
    • 2018-03-30
    • 2011-11-11
    • 1970-01-01
    相关资源
    最近更新 更多