当前位置: 首页 > news >正文

从C++到wasm,并在JavaScript中调用

我将为你提供一个完整的从 C++ 编译到 WebAssembly 并在 JavaScript 中调用的指南。

环境准备

1. 安装 Emscripten

# 克隆 emsdk 仓库
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk# 安装最新版本
./emsdk install latest
./emsdk activate latest# 设置环境变量
source ./emsdk_env.sh# 验证安装
emcc --version

基础示例

步骤 1:编写 C++ 代码

math_operations.cpp

#include <emscripten.h>
#include <cmath>// 使用 extern "C" 防止 C++ 名称修饰
extern "C" {EMSCRIPTEN_KEEPALIVEint add(int a, int b) {return a + b;}EMSCRIPTEN_KEEPALIVEdouble multiply(double a, double b) {return a * b;}EMSCRIPTEN_KEEPALIVEdouble squareRoot(double x) {return sqrt(x);}EMSCRIPTEN_KEEPALIVEint factorial(int n) {if (n <= 1) return 1;return n * factorial(n - 1);}
}

步骤 2:编译为 WebAssembly

# 基础编译命令
emcc math_operations.cpp \-o math_operations.js \-s WASM=1 \-s EXPORTED_FUNCTIONS='["_add", "_multiply", "_squareRoot", "_factorial"]' \-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'# 或者使用更现代的 ES6 模块方式
emcc math_operations.cpp \-o math_operations.mjs \-s WASM=1 \-s MODULARIZE=1 \-s EXPORT_ES6=1 \-s EXPORTED_FUNCTIONS='["_add", "_multiply", "_squareRoot", "_factorial"]' \-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'

在 JavaScript 中调用

方式 1:使用传统胶水代码

index.html

<!DOCTYPE html>
<html>
<head><title>WASM C++ 示例</title>
</head>
<body><h1>WebAssembly C++ 函数调用示例</h1><div><button onclick="testAdd()">测试加法</button><button onclick="testMultiply()">测试乘法</button><button onclick="testSquareRoot()">测试平方根</button><button onclick="testFactorial()">测试阶乘</button></div><div id="output" style="margin-top: 20px; padding: 10px; border: 1px solid #ccc;"></div><!-- 加载 Emscripten 生成的 JS 胶水代码 --><script src="math_operations.js"></script><script>let add, multiply, squareRoot, factorial;// 等待 WASM 模块初始化完成Module.onRuntimeInitialized = function() {// 使用 cwrap 包装 C++ 函数add = Module.cwrap('add', 'number', ['number', 'number']);multiply = Module.cwrap('multiply', 'number', ['number', 'number']);squareRoot = Module.cwrap('squareRoot', 'number', ['number']);factorial = Module.cwrap('factorial', 'number', ['number']);logOutput("WASM 模块加载完成!");};function testAdd() {if (!add) return;const result = add(15, 27);logOutput(`add(15, 27) = ${result}`);}function testMultiply() {if (!multiply) return;const result = multiply(3.14, 2.5);logOutput(`multiply(3.14, 2.5) = ${result}`);}function testSquareRoot() {if (!squareRoot) return;const result = squareRoot(16);logOutput(`squareRoot(16) = ${result}`);}function testFactorial() {if (!factorial) return;const result = factorial(5);logOutput(`factorial(5) = ${result}`);}function logOutput(message) {const output = document.getElementById('output');output.innerHTML += message + '<br>';}</script>
</body>
</html>

方式 2:使用 ES6 模块(推荐)

modern-example.html

<!DOCTYPE html>
<html>
<head><title>WASM ES6 模块示例</title>
</head>
<body><h1>现代 ES6 模块方式</h1><div id="output"></div><script type="module">import createModule from './math_operations.mjs';class MathWASM {constructor() {this.module = null;this.initialized = false;}async init() {try {this.module = await createModule();this.initialized = true;this.log("WASM 模块初始化成功!");// 包装所有函数this.add = this.module.cwrap('add', 'number', ['number', 'number']);this.multiply = this.module.cwrap('multiply', 'number', ['number', 'number']);this.squareRoot = this.module.cwrap('squareRoot', 'number', ['number']);this.factorial = this.module.cwrap('factorial', 'number', ['number']);this.runTests();} catch (error) {this.log(`初始化失败: ${error}`);}}runTests() {if (!this.initialized) return;this.log(`加法测试: 8 + 12 = ${this.add(8, 12)}`);this.log(`乘法测试: 4.5 × 3.2 = ${this.multiply(4.5, 3.2)}`);this.log(`平方根测试: √25 = ${this.squareRoot(25)}`);this.log(`阶乘测试: 6! = ${this.factorial(6)}`);}log(message) {const output = document.getElementById('output');output.innerHTML += message + '<br>';}}// 初始化 WASM 模块const mathWASM = new MathWASM();mathWASM.init();</script>
</body>
</html>

处理字符串和内存操作

C++ 代码(字符串处理)

string_operations.cpp

#include <emscripten.h>
#include <cstring>
#include <string>extern "C" {EMSCRIPTEN_KEEPALIVEint stringLength(const char* str) {return strlen(str);}EMSCRIPTEN_KEEPALIVEvoid reverseString(char* str, int length) {int start = 0;int end = length - 1;while (start < end) {char temp = str[start];str[start] = str[end];str[end] = temp;start++;end--;}}EMSCRIPTEN_KEEPALIVEchar* concatenateStrings(const char* str1, const char* str2) {std::string result = std::string(str1) + std::string(str2);char* output = (char*)malloc(result.length() + 1);strcpy(output, result.c_str());return output;}
}

JavaScript 调用字符串函数

string-example.html

<!DOCTYPE html>
<html>
<head><title>WASM 字符串操作</title>
</head>
<body><script src="string_operations.js"></script><script>Module.onRuntimeInitialized = function() {// 包装函数const stringLength = Module.cwrap('stringLength', 'number', ['string']);const reverseString = Module.cwrap('reverseString', null, ['number', 'number']);const concatenateStrings = Module.cwrap('concatenateStrings', 'number', ['string', 'string']);// 测试字符串长度const testStr = "Hello WebAssembly";console.log(`"${testStr}" 的长度: ${stringLength(testStr)}`);// 测试字符串反转const strToReverse = "Hello World";const lengthBytes = Module.lengthBytesUTF8(strToReverse) + 1;const buffer = Module._malloc(lengthBytes);Module.stringToUTF8(strToReverse, buffer, lengthBytes);reverseString(buffer, strToReverse.length);const reversed = Module.UTF8ToString(buffer);console.log(`反转 "${strToReverse}": ${reversed}`);Module._free(buffer);// 测试字符串连接const ptr = concatenateStrings("Hello ", "WASM");const concatenated = Module.UTF8ToString(ptr);console.log(`连接结果: ${concatenated}`);Module._free(ptr); // 记得释放内存};</script>
</body>
</html>

编译优化选项

生产环境编译

# 优化编译(推荐用于生产环境)
emcc math_operations.cpp \-o math_operations.js \-O3 \-s WASM=1 \-s MODULARIZE=1 \-s EXPORT_ES6=1 \-s EXPORTED_FUNCTIONS='["_add", "_multiply", "_squareRoot", "_factorial"]' \-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' \-s ALLOW_MEMORY_GROWTH=1

最小化编译

# 最小化文件大小
emcc math_operations.cpp \-o math_operations.js \-Oz \-s WASM=1 \-s MODULARIZE=1 \-s SINGLE_FILE=1 \-s EXPORTED_FUNCTIONS='["_add", "_multiply", "_squareRoot", "_factorial"]' \-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'

运行步骤

  1. 编译 C++ 代码

    emcc math_operations.cpp -o math_operations.js -s WASM=1 -s EXPORTED_FUNCTIONS='["_add", "_multiply", "_squareRoot", "_factorial"]' -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'
    
  2. 启动本地服务器(由于浏览器安全限制,不能直接打开 HTML 文件):

    # 使用 Python
    python -m http.server 8000# 或使用 Node.js
    npx http-server
    
  3. 在浏览器中访问

    http://localhost:8000
    

关键要点

  1. 使用 extern "C":防止 C++ 名称修饰
  2. 使用 EMSCRIPTEN_KEEPALIVE:确保函数被导出且不被优化掉
  3. 正确编译参数:确保导出需要的函数和运行时方法
  4. 异步加载:WASM 模块需要时间初始化
  5. 内存管理:对于返回指针的函数,记得在 JavaScript 中释放内存

这个完整的流程应该能帮助你成功地将 C++ 代码编译为 WebAssembly 并在 JavaScript 中调用。

http://icebutterfly214.com/news/26871/

相关文章:

  • 2025年评价高的冷库提升机TOP品牌厂家排行榜
  • Flask的核心知识点如下
  • 2025年比较好的行星减速器厂家选购指南与推荐
  • 2025年口碑好的激光切割比例阀厂家选购指南与推荐
  • 我的博客
  • 领码方案|微服务与SOA的世纪对话(5):未来已来——AI 驱动下的智能架构哲学 - 详解
  • 目前成都低压电缆工厂推荐榜top10
  • 【DNN】基础环境搭建 - 指南
  • 解码lvgl图片
  • 2025年口碑好的直角中空旋转平台TOP实力厂家推荐榜
  • 实用指南:探索大语言模型(LLM): 大模型应用与对应的硬件选型一览表
  • 逆向基础--C++数据类型 (02)
  • 2025年正规的仪器计量校准厂家最新用户好评榜
  • Ubuntu 软件安装中心闪退
  • 2025年评价高的专利评估综合口碑榜
  • 2025年优质的青年鸡高评价榜
  • day07-一键生成儿歌视频工作流
  • 中文机器阅读理解数据集:7000条高质量问答数据,涵盖搜索与知道双场景,支持DESCRIPTION:YES_NO:ENTITY多类型问题,适用于BERT:GPT等模型训练与评估
  • 2025年11月北京geo优化公司推荐榜:五强服务链路深度拆解
  • 实用指南:计算机视觉——从YOLO系列演进到YOLOv12架构创新、注意力机制优化、推理实践与性能基准
  • WGCLOUD可以监控群晖吗
  • [Linux框架编程——Lesson11.进程控制:等待]
  • 2025年正规的短视频拍摄热门选择排行榜
  • 2025年11月苏州医疗纠纷律师推荐周旭昊医学法学融合示范
  • 2025年值得信赖的装修最新热门推荐榜
  • AI Agent 的几个主要发展方向和细分领域
  • 2025年中国离婚房产律师联系电话推荐:经典案例与贴心沟通
  • 2025年北京离婚房产律师联系电话推荐:靠谱渠道与预约技巧
  • 2025年石棉橡胶板厂家联系电话推荐:华北货源一站汇总
  • 2025年靠谱的目视化规划施工最新畅销推荐榜供应商