首先要在Cmake工程中的cmakelists.txt文件中引入Emscripten工具链:
set(CMAKE_TOOLCHAIN_FILE "D:/CppPkg/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake")
直接看C++代码:
#include <emscripten/emscripten.h>
#include <cstdlib>
extern "C" {
struct DataResult {
double* dArr;
int propCount;
};
EMSCRIPTEN_KEEPALIVE
double sumArray(double* fArr, int arrLen) {
double sum = 0.0;
for (int i = 0; i < arrLen; i++) {
sum += fArr[i];
}
return sum;
}
EMSCRIPTEN_KEEPALIVE
double* vectorSum(double* vectorA, double* vectorB, int vecLen) {
double* resArr = (double*)malloc(vecLen * sizeof(double));
for (int i = 0; i < vecLen; i++) {
resArr[i] = vectorA[i] + vectorB[i];
}
return resArr;
}
EMSCRIPTEN_KEEPALIVE
DataResult* createDataResult(double* dArr, int propCount) {
DataResult* dtRes = (DataResult*)malloc(sizeof(DataResult));
dtRes->dArr = dArr;
dtRes->propCount = propCount;
return dtRes;
}
EMSCRIPTEN_KEEPALIVE
double* allocateDoubleMemory(int size) {
return (double*)malloc(size * sizeof(double));
}
EMSCRIPTEN_KEEPALIVE
void freeMemory(void* dataPtr) {
free(dataPtr);
}
}
通过em++命令,生成MyLib.js和MyLib.wasm文件
em++ MyLib.cpp -O2 -o MyLib.js -s WASM=1 -s MODULARIZE=1 -s EXPORT_NAME="MyLibModule" -s "EXPORTED_FUNCTIONS=['_sumArray']" -s "EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']"
在WASMTest.ts中编写代码:
type ImportObject = WebAssembly.Imports;
interface MyLibModule{
sumArray: (fArr: number, arrLen: number) => number;
allocateDoubleMemory: (size: number) => number;
freeMemory: (dataPtr: number) => void;
vectorSum: (vectorA: number, vectorB: number, vecLen: number) => number;
createDataResult: (dArrPtr: number, propCount: number) => number;
memory: WebAssembly.Memory;
}
async function loadWasmModule(memory: WebAssembly.Memory): Promise<MyLibModule>{
const importObject: WebAssembly.Imports = {
env: {
memory: memory,
emscripten_resize_heap: () => {
console.log("emscripten_resize_heap called");
return true; // 这里可以返回 true 或者处理扩展内存的逻辑
},
}
};
const response = await fetch('src/lib/wasm/MyLib.wasm');
const buffer = await response.arrayBuffer();
const module = await WebAssembly.instantiate(buffer, importObject);
return module.instance.exports as unknown as MyLibModule;
}
async function useModule(){
const memory = new WebAssembly.Memory({ initial: 20, maximum: 1000 });
const wasmModule = await loadWasmModule(memory);
const pageSize = 64 * 1024;
const totalMemory = memory.buffer.byteLength;
const totalPages = totalMemory/pageSize;
console.log(`Total memory: ${totalMemory} bytes (${totalPages} pages)`)
memory.grow(100);
console.log(`New total memory size: ${memory.buffer.byteLength} bytes`);
const vecA = new Float64Array([1,2,3,4,5,6,9,12]);
const vecB = new Float64Array([1,2,3,4,5,6,6,14]);
const vecAptr = wasmModule.allocateDoubleMemory(vecA.length * 8);
const vecBptr = wasmModule.allocateDoubleMemory(vecB.length * 8);
const memoryBuffer = wasmModule.memory.buffer;
const wasmVecA = new Float64Array(memoryBuffer, vecAptr, vecA.length);
const wasmVecB = new Float64Array(memoryBuffer, vecBptr, vecB.length);
wasmVecA.set(vecA);
wasmVecB.set(vecB);
const resultPtr = wasmModule.vectorSum(vecAptr, vecBptr, vecA.length);
const wasmRes = new Float64Array(memoryBuffer, resultPtr, vecA.length);
console.log(wasmRes)
wasmModule.freeMemory(vecAptr);
wasmModule.freeMemory(vecBptr);
wasmModule.freeMemory(resultPtr);
// 传递数组,返回结构体指针,从结构体指针中取数据。
const dArr = new Float64Array([5,4,7,1,9,3,5]);
const dArrPtr = wasmModule.allocateDoubleMemory(dArr.length * 8);
const wasmdArr = new Float64Array(memoryBuffer, dArrPtr, dArr.length);
wasmdArr.set(dArr);
const resPtr = wasmModule.createDataResult(dArrPtr, dArr.length);
const dataView = new DataView(memoryBuffer);
const dArrResPtr = Number(dataView.getUint32(resPtr, true));
const propCount = dataView.getInt32(resPtr+4, true);
const resdArr = new Float64Array(memoryBuffer, dArrResPtr, propCount);
console.log(`return struct DataResult dArr Pointer is ${dArrResPtr}`);
console.log(`the struct DataResult dArr value is: `);
console.log(resdArr);
wasmModule.freeMemory(dArrPtr);
wasmModule.freeMemory(resPtr);
}
useModule();
查看浏览器打印结果:
第一个例子:在这里,我们传递了2个双精度浮点的数组,传递进入C++函数,然后给两个数组求和,返回的是double*类型的指针。
第二个例子:
最后是返回一个C++结构体指针,在TypeScript中通过DataView来获取内存中的地址偏移量来取得结构体中的数据,因为em++编译器是以32位来编译.cpp文件的,所以指针占4个字节。DataResult结构体第一个属性dArr是一个指针,那么就是地址偏移量0,然后以32位无符号整型取得dArr指针,通过这个指针去读取wasm中memoryBuffer中的数据,就可以得到dArr属性对应的值了。同理,通过地址偏移量+4来得到propCount属性,以32位整数读取数据即可。