Подтвердить что ты не робот

Как обрабатывать передающие/возвращающие указатели массива на emscripten скомпилированный код?

Я новичок в 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;
}

Используя опции -o для emcc, я сгенерировал файл .html, который я загрузил в браузер (Chrome).

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

Однако при использовании команды module.cwrap() через консоль javascript и попытке вызвать функцию напрямую (вне 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   

У меня есть следующие вопросы:

  • Я правильно ли использую синтаксис для возвращаемого типа и списка параметров в моем вызове Module.cwrap()? Я успешно выполнил простой, прямолинейный пример int_sqrt() в разделе "Взаимодействие с кодом" в учебнике, посвященном передаче не указательных переменных в процедуру int_sqrt().

  • Есть ли что-то другое, что происходит, когда массивы и/или указатели передаются (или возвращаются) сгенерированным emscripten кодом javascript?

  • Как получается, что сгенерированный вывод в браузере функции displayArray() работает (как и ожидалось) при вызове из main(); но не через консоль javascript?

Я новичок в Emscripten/javascript, поэтому вся информация/помощь будут очень благодарны.

Спасибо,

ФК

4b9b3361

Ответ 1

Ожидаемый формат Module.cwrap позволяет передать массив в функцию, но будет утверждать результат и потерпеть неудачу, если вы попытаетесь вернуть массив

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, который будет reset после выполнения выведенной функции, что сделает возвращаемое смещение Array потенциально непригодным для использования, поскольку оно освобождает стеки.

Гораздо более предпочтительно, если вы хотите получить результаты своей функции, выделить и сохранить массив внутри системы Emscriptens Heap.

Код Emscripten доступен только для доступа к памяти, которая была выделена в пространстве Emscripten Heap. Массивы, которые вы пытаетесь передать в функцию, выделяются за пределами кучи, с которой выполняется код Emscripten, и не соответствуют ожидаемому в исходных аргументах типа необработанного указателя.

Существует несколько способов получить доступ к массиву для передачи данных в ваши функции. Все это требует, чтобы Emscripen знала расположение вашей памяти внутри emscripten Module.HEAP *, поэтому в какой-то момент начальный шаг вызывается функцией Emscripten "_malloc".

var offset = Module._malloc(24)

Это позволит вам выделить необходимые 24 байта в куче Emscripten, необходимой для вашего трехмерного 8-байтового массива с тремя байтами, и возвращает смещение Number в куче Emscripten, обозначающее смещение Typharray U8, зарезервированное для вашего массива. Это смещение является вашим указателем и будет автоматически работать в вашей функции displayArray cwrap, когда он настроен на использование смещений исходного указателя.

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

В этот момент, если вы хотите получить доступ к содержимому массива или изменить его до тех пор, пока malloc действителен, у вас есть как минимум следующие параметры:

  • Задайте память с помощью временно упакованного массива Float64 без простого способа восстановить значение, за исключением следующих двух методов доступа

    Module.HEAPF64.set(new Float64Array([1,2,3]), offset/8);
    displayArray(offset);
    
  • 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
    
  • Если вы хотите более подробно использовать указатель на стороне Javascript, вы можете вручную вывести Subarray TypedArray из записи HEAPF64. Это позволяет вам легко считывать значения, как только вы закончите выполнение своей функции. Этот 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 byte offset when calling the function
    displayArray(offset);
    //doublePtr[] now contains the 98,99,100 values
    

Ответ 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

Ответ 3

Во время работы по переносу библиотеки с C на JS/WASM и созданию веб-интерфейса для нее мы создали c2JsGenerator