初探 Wasm 移植

公司项目中曲线构建功能------根据进入和退出车道线计算出转弯处的车道线------由后端用 C++ 实现,先前尝试移植到 Wasm 以运行在浏览器上,本文为该次移植的部分记录。

CMake、GCC、Emscripten SDK 可自行安装。

VS Code 配置可参考 Using C++ on Linux in VS Code,CMake 相关扩展可自行安装。

使用 C++ SDK

由于相关代码封装在一个 C++ SDK 里面,因此首先需要在本地使用该 SDK 实现一个曲线构建的例子。公司使用的是自研的 C++ 包管理工具,仿照 C++ SDK 里面的 CMakeLists.txt、项目配置文件、构建脚本编写相应文件即可。其中最关键的两条 CMake 命令如下:

bash 复制代码
cmake .. # 配置
make # 构建

C++ 包安装在系统根目录下,为了在开发时让 IDE 识别出头文件,需要将包的安装路径添加到 .vscode/c_cpp_properties.json 的 includePath 中。

如果 CMake 配置时找不到包,可检查库的查找路径是否正确:

bash 复制代码
echo $LD_LIBRARY_PATH

实践中遇到的问题是包名的命名风格,重点检查 CMakeLists.txt 中 "_" 和 "-" 是否正确:

  1. find_package 使用的包名需要和包的 .cmake 文件名保持一致。
  2. target_link_libraries 使用的包名需要和包的 .so 文件名保持一致。

同一个包的 .cmake、.so 文件名中的连接符可能不同。

如果链接时找不到符号并报错 "undefined reference to",可使用 nm 查找相应名字并 demangle,确认所使用的名字是否存在:

bash 复制代码
nm --demangle ×××.so | grep <reference name>

编译为 Wasm

我们所安装的包是这种动态链接库文件:×××-x86_64-linux-gnu.so。个人理解,这种文件已经是针对 x86 架构编译过了,应该没办法再转为 Wasm。要使最终编译为 Wasm,目前的做法是复制依赖库的 C++ 源码,除了标准库外,移除所有外部依赖,并删掉没有用到的代码和 CMakeLists.txt 中对无用依赖的链接。

研究 emscripten-ports 上面的库的移植方式可能也是一条思路,但这方面尚未开展任何调研。

移植到 Emscripten 时,根据编译时报错做了如下处理:

为了让 IDE 找到 Emscripten SDK 的头文件,还需要对 includePath 做相应修改。

  • CMake 命令

    bash 复制代码
    emcmake cmake .. # 配置
    emmake make # 构建
  • CMakeLists.txt

    • 编译和链接时都需要的 flags 可通过 CMAKE_CXX_FLAGS 来设置:

      cmake 复制代码
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -sUSE_BOOST_HEADERS=1")
    • 只在链接时才需要的 flags 可通过 set_target_properties 指定 LINK_FLAGS

      cmake 复制代码
      set_target_properties(demo PROPERTIES LINKER_LANGUAGE CXX LINK_FLAGS "-sWASM=1 -sEXPORTED_RUNTIME_METHODS=cwrap,writeArrayToMemory -sEXPORTED_FUNCTIONS=_malloc,_free -sEXPORT_ES6=1")

      只在编译时才需要的 flags 可以通过 COMPILE_FLAGS 来设置。

  • bits/stdc++.h

    原代码使用了 bits/stdc++.h 头文件,里面包含了很多标准库头文件,方便 #include(据说竞赛中常用),但它并不是 C++ 标准,无法移植。如果按照先前的步骤已经将依赖 bits/stdc++.h 的代码移除,则可以直接删掉 #include <bits/stdc++.h>,或者直接复制 bits/stdc++.h 头文件中的内容。

  • uint

    原代码使用了类型别名 uint,它应该也不是 C++ 标准,因此需要在相应位置补充类型别名:

    cpp 复制代码
    typedef unsigned int uint;
  • USE_BOOST_HEADERS

    由于原代码使用了 boost 库,而 boost 已经移植到了 Emscripten,因此需要在编译、链接时添加:

    bash 复制代码
    -sUSE_BOOST_HEADERS=1

可能是由于所移植的代码只在内存中做数值计算,没有访问系统资源,原代码中绝大部分都无需改动。编译出 Wasm 之后,可将 Wasm 执行结果与原 C++ 执行结果进行对比,确认是否一致。

上图是 U 型掉头口场景,实际上还适用于任何路口场景。

相关推荐
椰萝Yerosius27 分钟前
[题解]2024CCPC郑州站——Z-order Curve
c++·算法
滨HI03 小时前
C++ opencv简化轮廓
开发语言·c++·opencv
学习路上_write4 小时前
FREERTOS_互斥量_创建和使用
c语言·开发语言·c++·stm32·单片机·嵌入式硬件
闻缺陷则喜何志丹5 小时前
【SOSDP模板 容斥原理 逆向思考】3757. 有效子序列的数量|分数未知
c++·算法·力扣·容斥原理·sosdp·逆向思考
BestOrNothing_20155 小时前
一篇搞懂 C++ 重载:函数重载 + 运算符重载,从入门到会用(含 ++、<<、== 实战)
c++·函数重载·运算符重载·operator·前置后置++·重载与重写
2501_941144426 小时前
Python + C++ 异构微服务设计与优化
c++·python·微服务
程序猿编码6 小时前
PRINCE算法的密码生成器:原理与设计思路(C/C++代码实现)
c语言·网络·c++·算法·安全·prince
charlie1145141917 小时前
深入理解C/C++的编译链接技术6——A2:动态库设计基础之ABI设计接口
c语言·开发语言·c++·学习·动态库·函数
Cx330❀7 小时前
C++ STL set 完全指南:从基础用法到实战技巧
开发语言·数据结构·c++·算法·leetcode·面试
zmzb01037 小时前
C++课后习题训练记录Day33
开发语言·c++