The post shows how to pass array from javascript to cpp function as parameter and how to use javascript to receive array from cpp function.
We have to know the javascript TypedArray objects and different types of emscripten memory model firstly.
JS TypedArray objects
Type | Value Range | Size in bytes | Description | Web IDL type | Equivalent C type |
Int8Array | -128 to 127 | 1 | 8-bit two’s complement signed integer | byte | int8_t |
Uint8Array | 0 to 255 | 1 | 8-bit unsigned integer | octet | uint8_t |
Uint8ClampedArray | 0 to 255 | 1 | 8-bit unsigned integer (clamped) | octet | uint8_t |
Int16Array | -32768 to 32767 | 2 | 16-bit two’s complement signed integer | short | int16_t |
Uint16Array | 0 to 65535 | 2 | 16-bit unsigned integer | unsigned short | uint16_t |
Int32Array | -2147483648 to 2147483647 | 4 | 32-bit two’s complement signed integer | long | int32_t |
Uint32Array | 0 to 4294967295 | 4 | 32-bit unsigned integer | unsigned long | uint32_t |
Float32Array | -3.4E38 to 3.4E38 and 1.2E-38 is the min positive number | 4 | 32-bit IEEE floating point number (7 significant digits e.g., 1.234567) | unrestricted float | float |
Float64Array | -1.8E308 to 1.8E308 and 5E-324 is the min positive number | 8 | 64-bit IEEE floating point number (16 significant digits e.g., 1.23456789012345) | unrestricted double | double |
BigInt64Array | -263 to 263 – 1 | 8 | 64-bit two’s complement signed integer | bigint | int64_t (signed long long) |
BigUint64Array | 0 to 264 – 1 | 8 | 64-bit unsigned integer | bigint | uint64_t (unsigned long long) |
Emscripten type accessors for the memory model
Types | Memory |
---|---|
HEAP8 | View for 8-bit signed memory |
HEAP16 | View for 16-bit signed memory |
HEAP32 | View for 32-bit signed memory |
HEAPU8 | View for 8-bit unsigned memory |
HEAPU16 | View for 16-bit unsigned memory |
HEAPU32 | View for 32-bit unsigned memory |
HEAPF32 | View for 32-bit float memory |
HEAPF64 | View for 64-bit float memory |
I create a demo program to test our example, the source code has been uploaded to GitHub, https://github.com/theArcticOcean/tutorials/tree/main/learnWebAssembly/passArrayToCPP.
Html:
<html>
<head>
<!-- Load WebAssembly module -->
<script type="text/javascript" src="build_wasm/passArrayToCPP.js"></script>
</head>
<body>
<div>
Result:
<span id="answer"/>
</div>
<button id="showBools">show bools</button><br>
<button id="intSum">int sum</button><br>
<button id="doubleSum">double sum</button><br>
<button id="intArray">int array</button><br>
<button id="floatArray">float array</button><br>
<script>
var worker;
var Module = {
onRuntimeInitialized: function () {
worker = new Module.Worker();
console.log( worker + " start work!" );
},
};
var app = createModule( Module );
showBools.onclick = function()
{
var values = new Int8Array([true, false, false, true, true]);
var heapSpace = Module._malloc(values.length * values.BYTES_PER_ELEMENT);
Module.HEAP8.set(values, heapSpace); // bool has 1 byte
worker.ShowBoolArray( heapSpace, values.length );
}
intSum.onclick = function()
{
var values = new Int32Array([1, 2, 3, 4, 5]);
var heapSpace = Module._malloc(values.length * values.BYTES_PER_ELEMENT);
Module.HEAP32.set(values, heapSpace>>2); // int has 4 bytes
const result = worker.GetIntSum( heapSpace, values.length );
console.log( "result " + result );
document.getElementById("answer").innerHTML = result;
}
doubleSum.onclick = function()
{
var values = new Float64Array([0.1, 0.2, 0.3, 0.4, 0.5]);
var heapSpace = Module._malloc(values.length * values.BYTES_PER_ELEMENT);
Module.HEAPF64.set(values, heapSpace>>3); // double has 8 bytes
const result = worker.GetDoubleSum( heapSpace, values.length );
console.log( "result " + result );
document.getElementById("answer").innerHTML = result;
}
intArray.onclick = function()
{
var heap = worker.GetIntArray();
const arrayData = []
for (let v=0; v < 3; v++) {
arrayData.push( Module.HEAP32[heap/Int32Array.BYTES_PER_ELEMENT+v] )
}
console.log( arrayData );
}
floatArray.onclick = function()
{
var heap = worker.GetFloatArray( 3 );
const arrayData = []
for (let v=0; v < 3; v++) {
arrayData.push( Module.HEAPF32[heap/Float32Array.BYTES_PER_ELEMENT+v] )
}
console.log( arrayData );
}
</script>
</body>
</html>
passArrayToCPP.h
#pragma once
#include <string>
class Worker
{
public:
Worker();
int GetIntSum(uintptr_t arrayBuffer, int size);
double GetDoubleSum(uintptr_t arrayBuffer, int size);
void ShowBoolArray(uintptr_t arrayBuffer, int size);
uintptr_t GetIntArray();
uintptr_t GetFloatArray(int size);
};
passArrayToCPP.cpp
#include "passArrayToCPP.h"
#include <iostream>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
int Worker::GetIntSum(uintptr_t arrayBuffer, int size)
{
std::cout << "GetIntSum start work:" << std::endl;
auto array = reinterpret_cast<int *>( arrayBuffer );
int sum = 0;
for( int i = 0; i < size; ++i )
{
sum = sum + array[i];
std::cout << array[i] << std::endl;
}
std::cout << "sum: " << sum << std::endl;
return sum;
}
double Worker::GetDoubleSum(uintptr_t arrayBuffer, int size)
{
std::cout << "GetDoubleSum start work:" << std::endl;
auto array = reinterpret_cast<double *>( arrayBuffer );
double sum = 0;
for( int i = 0; i < size; ++i )
{
sum = sum + array[i];
std::cout << array[i] << std::endl;
}
std::cout << "sum: " << sum << std::endl;
return sum;
}
void Worker::ShowBoolArray(uintptr_t arrayBuffer, int size)
{
auto array = reinterpret_cast<bool *>( arrayBuffer );
for( int i = 0; i < size; ++i )
{
std::cout << "bool: " << array[i] << std::endl;
}
}
uintptr_t Worker::GetIntArray()
{
int *values = new int[3];
values[0] = 1;
values[1] = 2;
values[2] = 3;
return uintptr_t( values );
}
uintptr_t Worker::GetFloatArray(int size)
{
float values[size];
for( int i = 0; i < size; ++i )
{
values[i] = i*2;
}
auto arrayPtr = uintptr_t( &values[0] );
return arrayPtr;
}
Worker::Worker()
{
std::cout << "generate Worker!" << std::endl;
}
binding.cpp
#include <emscripten/bind.h>
#include "passArrayToCPP.h"
using namespace emscripten;
EMSCRIPTEN_BINDINGS(worker) {
class_<Worker>("Worker")
.constructor()
.function("GetIntSum", &Worker::GetIntSum)
.function("GetDoubleSum", &Worker::GetDoubleSum)
.function("ShowBoolArray", &Worker::ShowBoolArray)
.function("GetIntArray", &Worker::GetIntArray)
.function("GetFloatArray", &Worker::GetFloatArray)
;
}
Let’s build the project for web page and test it.
cd <project-path>
mkdir build_wasm
cd build_wasm/
emcmake cmake ../
make -j8
Set up a small http server to view.
cd <project-server>
python -m http.server 4000
Test our web page, it seems right.
Thanks for writing this.
But compiling the example with emscripten 3.1.41 works, but gives me:
in the index.html for “show bools”, “int sum” and “double sum”.
The buttons for “int array” and “float array” work as expected.
Found a solution, replacing in index.html:
with
Yep, maybe my emscripten version is different with yours. Thanks for your comments.