首先要在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_KEEPALIVEdouble sumArray(double* fArr, int arrLen) {double sum = 0.0;for (int i = 0; i < arrLen; i++) {sum += fArr[i];}return sum;}EMSCRIPTEN_KEEPALIVEdouble* 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_KEEPALIVEDataResult* createDataResult(double* dArr, int propCount) {DataResult* dtRes = (DataResult*)malloc(sizeof(DataResult));dtRes->dArr = dArr;dtRes->propCount = propCount;return dtRes;}EMSCRIPTEN_KEEPALIVEdouble* allocateDoubleMemory(int size) {return (double*)malloc(size * sizeof(double));}EMSCRIPTEN_KEEPALIVEvoid 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位整数读取数据即可。