Web Assembly 构建 C++ 代码在 JavaScript 环境调用

Web Assembly 构建 C++ 代码在 JavaScript 环境调用

Emscripten 环境准备

  1. 拉取核心驱动脚本
bash 复制代码
# Get the emsdk repo
git clone https: //github.com/emscripten-core/emsdk.git

# Enter that directory
cd emsdk
  1. 获取 Emscripten 最新版工具并配置环境
bash 复制代码
# Fetch the latest version of the emsdk (not needed the first time you clone)
git pull

# Download and install the latest SDK tools.
./emsdk install latest

# Make the "latest" SDK "active" for the current user. (writes .emscripten file)
./emsdk activate latest

# Activate PATH and other environment variables in the current terminal
source ./emsdk_env.sh

工具测试

在目录 emsdk/test/下新建一个 test.c 文件,内容如下:

c 复制代码
#include <stdio.h>

int main() {
  printf("hello, FengCh!\n");
  return 0;
}

在终端中运行代码 emcc test/test.c -o test.wasm.js ,最终在目录下生成一个 test.wasm.js js 文件和 test.wasm.wasm 的二进制文件:

通过 node 运行 test.wasm.js 文件,终端输出"hello,FengCh":

demo

文件目录:

python 复制代码
.
├── compile                         # 编译目录
│   ├── build_wasm.py               # C++ 编译脚本
│   ├── c++                         # C++ 代码
│   │   └── communication.cpp
│   └── js                          # js 代码
│       └── utils.js
└── src                             
    └── main.js

下面 C++ 代码中有一个 Communication 类,类里面有两个方法, 其中 executeCb 方法内编写了 js 代码并输出一段信息,且最终编译后会在 js 层调用,monitorCb 方法在编译后由 js 层调用,并传入一个回调函数和一个 isExecute 参数,最终通过 EMSCRIPTEN_BINDINGS 将 C++ 环境的类与成员函数绑定到 JavaScript 环境中:

cpp 复制代码
#include <emscripten.h>  
#include <emscripten/bind.h>  

class Communication { 
public:  
    void executeCb() {  
        EM_ASM_({  
            console.log('这是 C++ 层的函数在 js 层被调用,且在 C++ 内嵌套 js 代码');  
        });  
    }

    void monitorCb(emscripten::val cb,bool isExecute){
        if(isExecute){
            cb();
        };
    } 
};

EMSCRIPTEN_BINDINGS(communication_module_bindings) {  
    emscripten::class_<Communication>("Communication")  
    .constructor<>() 
    .function("executeCb", &Communication::executeCb)
    .function("monitorCb", &Communication::monitorCb); 
}

编写如下 Python 脚本编译 C++ 代码,其中 source_wasm_build_path 字段包含了要编译的 C++ 代码路径,emcc_wasm_build_args 字段指定了 Emscripten 编译参数,最终根据编译结果分别输出对应信息:

py 复制代码
import os

source_wasm_build_path =  ' \./../compile/c++/communication.cpp'

emcc_wasm_build_args = ' \
--post-js ./js/utils.js \
-o ../src/communication.wasm.js'
command_wasm_build = 'emcc -lembind -s WASM=1' + source_wasm_build_path + emcc_wasm_build_args
result = os.system(command_wasm_build)
print(command_wasm_build)
if result == 0:
    print('Build wasm success')
else:
    print('Build wasm failed')
    exit(1)

进入 compile 目录下终端输入命令 python build_wasm.py 并运行:

编译成功后在 src 目录下多出 编译后的 js 文件和 wasm 二进制文件:

在 main.js 中可以通过引入生成的胶水文件 communication.wasm.js 在 Node 环境下调用 C++ 层的代码,代码如下:

js 复制代码
const Module = require("./communication.wasm.js");

// Wasm模块加载并初始化完成后被调用的回调函数
Module.onRuntimeInitialized = (_) => {
  const communicateModule = new Module.Communication();
  
  // 调用 C++ 层的函数
  communicateModule.executeCb();
  
  // js 层的函数在 C++ 层被调用
  communicateModule.monitorCb(() => {
    console.log("这是 js 层的函数在 C++ 层被调用");
  }, true);
  
  // 编译 C++ 代码时在末尾插入的额外的 js 代码
  const result = Module.safelyParse("{name: 'icebergfeng'}");
  console.log(result);
};

在 compile 目录执行 node ../src/main.js 得到结果如下:

相关推荐
前端小魔女14 天前
Rust赋能前端:为WebAssembly 瘦身
前端·rust·webassembly
码力码力我爱你20 天前
QT + WebAssembly + Vue环境搭建
vue.js·vue·wasm·webassembly·emscripten
码力码力我爱你24 天前
Vue Application exit (SharedArrayBuffer is not defined)
linux·前端·javascript·qt·vue·wasm·webassembly
Atypiape21 个月前
Webpack 5 支持访问 Rust WebAssembly 线性内存
javascript·rust·wasm·webassembly
大宝贱2 个月前
基于ffmpeg.wasm实现的视频格式转换工具
ffmpeg·wasm·webassembly·vitepress·视频格式转换
vivo互联网技术3 个月前
基于 Three.js 的 3D 模型加载优化
rust·three.js·webassembly·3d模型·web3d
得物技术3 个月前
前端打包工具Mako架构解析|得物技术
前端·rust·webassembly
Aaaaaaaaaaayou3 个月前
从零实现 React v18,但 WASM 版 - [18] 实现 useRef, useCallback, useMemo
react.js·rust·webassembly