javascript 如何处理传递/返回数组指针到 emscripten 编译代码?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17883799/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 09:59:17  来源:igfitidea点击:

How to handle passing/returning array pointers to emscripten compiled code?

javascriptemscripten

提问by user2618901

I am new to Emscripten/javascript as well as this Overstack community. I apologize, in advance, if my situation has already been addressed.

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

From a windows 7 environment, I've used emcc to compile a simple c program which accepts an array and modifies it (see below).

在 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;
}

Using the -o options for emcc, I generated a .html file which I loaded to a browser (Chrome).

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

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

Upon loading, I see that the output being generated within the browser window is as expected (see below).

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

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

However, when using the module.cwrap() command via javascript console and attempting to invoke the function directly (outside of main()) ,

但是,当通过 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]

I am seeing the following being generated/displayed in the browser which is NOT what I expect to see.

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

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   

I have the following questions:

我有以下问题:

  1. Do I have the syntax correct for the return type and parameter listing correct in my call to Module.cwrap()? I've successfully run the simple, straight-forward example of int_sqrt() in the "Interacting with code" section of the tutorial which deals with passing non-pointer variables to the int_sqrt() routine.

  2. Is there something different that is happening when arrays and/or pointers are passed to (or returned from) the emscripten-generated javascript code?

  3. How is that the generated output in the browser of the function, displayArray(), works (as expected) when called from main(); but not via the javascript console?

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

  2. 当数组和/或指针被传递到(或从)emscripten 生成的 javascript 代码时,是否发生了一些不同的事情?

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

I am new to Emscripten/javascript so any info/assistance will be greatly appreciated.

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

Thank you,

谢谢,

FC

FC

回答by phoenixillusion

The expected format of Module.cwrap does allow for 'array's to be passed into the function, but will assert on result and fail if you attempt to return an array

Module.cwrap 的预期格式确实允许将“数组”传递到函数中,但如果您尝试返回数组,则会对结果进行断言并失败

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

A second restriction of this is that incoming arrays are expected to be byte arrays, meaning you would need to convert any incoming double arrays into unsigned 8-bit numbers

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

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

Calling the method this way will invoke your function, temporarily copying your arrays to the Emscripten stack which will be reset after your invoked function's execution, making the returned Array offset potentially unusable as it is freed stackspace.

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

It is much more preferable, if you want the results of your function, to allocate and preserve an array inside Emscriptens Heap system.

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

Emscripten code is only able to access memory that has been allocated within Emscripten's Heap space. The arrays that you are attempting to pass into the function are being allocated outside the heap that the Emscripten code is running against, and do not match the raw pointer type expected in the incoming arguments.

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

There are several ways that you can gain access to an array to pass data to your functions. All of these require Emscripen having knowledge of the location of your memory inside the emscripten Module.HEAP*, so the initial step is at some point to call the Emscripten "_malloc" function.

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

var offset = Module._malloc(24)

This would allow you to allocate the required 24 bytes in the Emscripten heap needed for your 3x 8-byte double array, and returns a Number offset in the Emscripten heap denoting the U8 TypedArray offset reserved for your array. This offset is your pointer, and will automatically work being passed into your cwrap displayArray function when it is configured to use the raw pointer offsets.

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

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

At this point, if you wish to access or modify the contents of the array, as long as the malloc is valid, you have at least the following options:

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

  1. Set the memory using a temporarily wrapped Float64 array, with no easy way to recover the value except the following 2 methods of access

    Module.HEAPF64.set(new Float64Array([1,2,3]), offset/8);
    displayArray(offset);
    
  2. Module.setValue will use the 'double' hint to automatically modify the HEAPF64 offset, divided by 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. If you wish to use your pointer more extensively on the Javascript side, you can pull a subarray TypedArray off the HEAPF64 entry manually. This allows you to easily read the values once you have finished executing your function. This TypedArray is backed by the same heap as the rest of the Emscripten, so all changes performed on the Javascript side will be reflected on the Emscripten side and vice-versa:

    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
    
  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
    

回答by dtasev

As a bonus to the other answer, here is a single convenience function for allocating a float64array

作为另一个答案的奖励,这里有一个用于分配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)

Gives the output from the original function:

给出原始函数的输出:

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