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 得到结果如下:

相关推荐
小猪猪屁11 天前
WebAssembly 从零到实战:前端性能革命完全指南
前端·vue.js·webassembly
pepedd86412 天前
WebAssembly简单入门
前端·webassembly·trae
受之以蒙15 天前
Rust & WebAssembly 实践:构建一个简单实时的 Markdown 编辑器
笔记·rust·webassembly
wayhome在哪17 天前
3 分钟上手!用 WebAssembly 优化前端图片处理性能(附完整代码)
javascript·性能优化·webassembly
yangholmes888820 天前
EMSCRIPTEN File System 入门
前端·webassembly
yangholmes888825 天前
如何在 web 应用中使用 GDAL (三)
前端·webassembly
yangholmes88881 个月前
如何在 web 应用中使用 GDAL (二)
前端·webassembly
yangholmes88881 个月前
如何在 web 应用中使用 GDAL (一)
webassembly
DogDaoDao1 个月前
WebAssembly技术详解:从浏览器到云原生的高性能革命
云原生·音视频·编译·wasm·webassembly·流媒体·多媒体
受之以蒙1 个月前
Rust & WebAssembly 性能调优指南:从毫秒级加速到KB级瘦身
笔记·rust·webassembly