掘金 后端 ( ) • 2024-04-23 10:42

Electron集成C++技术选型

技术论证结果:

通过node-gyp实现js和c++相互调用传参,后期再与Electron集成,直接写JS即可。

选型论证需求

研究下c++如何与Electron集成(初步看有两种方案,nodejs集成c++,或者将c++编译wasm,了解两种方案的优劣)

  1. 实现js中调用c++函数
  2. 实现c++中调用js函数 需要探索参数传递问题,除了简单的类型,还得看一些复杂的参数如何传递,如对象、函数 写几个demo进行验证,出一篇c++与Electron集成的详细文档

选型方案一:nodejs集成c++(优选)

1. 技术方案特点

Node.js 集成 C++ 是通过 Node.js 的插件机制,将 C++ 模块导入到 Electron 应用中,并实现 JavaScript 与 C++ 的互操作。

2. 优缺点分析

优点:

  • 直接利用 Node.js 的插件机制,简化了与 Electron 的集成过程,方便直接调用。

缺点:

  • 需要熟悉 Node.js 的插件开发,可能涉及 V8 引擎的 API 调用和内存管理,常规开发基本不涉及。

3. 选择理由

对于 Electron 集成 C++ 的首选技术方案,我会倾向于选择 Node.js 集成 C++,理由:

Electron中可以直接使用通过使用JS调用 C++,利用了 Node.js 的插件(node-gyp)机制,与 Electron 的集成相对简单直接。相比之下,将 C++ 编译为 Wasm 需要额外的工作和配置,同时直接进行语法编译转换,不方便调试,而且可能存在一些限制和兼容性问题。

选型方案二:c++编译wasm

1. 技术方案特点

将 C++ 代码编译为 WebAssembly (Wasm) 格式,可以在浏览器中直接运行,并与 JavaScript 高度集成,大致执行顺序1. 加载框架的 js 文件;2. 框架 js 文件自动加载并编译 wasm 文件;3. 执行 demo 文件。

2. 优缺点分析

优点:

  • 跨平台、高性能,与 JavaScript 高度集成,适用于前端和后端代码共享。
  • 避免了 Node.js 插件开发的复杂性。

缺点:

  • 需要安装额外C++编译环境,如使用 Emscripten 进行编译,直接进行语法编译后还需要进行加载调用wasm,才能使用wasm文件,相当于是将c++语法转化成js语法,编译调试起来困难。
  • WebAssembly 目前仍在发展中,可能存在一些限制和兼容性问题。

选型方案一:具体实现

1. 开发环境

电脑环境

window10

node环境

node版本 17.0.1 
npm install -g node-gyp // node-gyp:Node.js 编写 C++ 扩展的构建工具

python环境

python-3.12.0

开发工具vscode

2. 示例说明

本demo示例分为两部分分别为js-cpp-xx和cpp-js-xx。

js-cpp-integration-demo说明:js调用c++文件,进行传值

cpp-js-integration-demo说明:cpp调用js文件,进行传值测试

3. 原理说明

addon.cpp文件中编写c++代码,binding.gyp文件

 |-- js-cpp-integration-demo
    |-- addon
        |-- build  // 打包和编译后的文件
        |-- addon.cpp // C++代码
        |-- binding.gyp // 编译配置
    |-- index.js // node入口文件,加载c++打包后文件 require('./addon/build/Release/addon.node');

4. 代码说明

代码执行步骤,以js-cpp-integration-demo为例:

# 导航到你的模块目录
cd xxx/demo/js-cpp-integration-demo/addon
​
npm install -g node-gyp
​
# 读取项目根目录下的 binding.gyp 文件,生成编译配置文件
node-gyp configure
​
# 通过配置文件编译原生Node.js模块
node-gyp build // *** 每次更改C++代码,需要重新编译 ***
​
cd xxx/demo/js-cpp-integration-demo
​
node ./index.js

index.js文件执行结果示例:

// index.js中打印结果示例:
1.Adding: 13
2.函数回调结果: 测试文字
3.Send(js)Array: [ [ 'aa', 'bb', 'cc', 'dd' ], [ 'aaa', 'bbb', 'ccc', 'ddd' ] ]
Received(cpp): [[aa,bb,cc,dd],[aaa,bbb,ccc,ddd]]
4.Send(js)Object: {
  amount: 99,
  letters: [ [ 'aa', 'bb', 'cc', 'dd' ], [ 'aaa', 'bbb', 'ccc', 'ddd' ] ]
}
Received(cpp): {
  amount: 99.000000,
  letters: [['aa', 'bb', 'cc', 'dd'], ['aaa', 'bbb', 'ccc', 'ddd']]
}

index.js中文件代码:

const addon = require('./addon/build/Release/addon');
// const addon = require('./addon/build/Debug/addon.node'); debugger使用
​
// 1. 调用cpp函数示例
console.log('1.Adding:', addon.add(3, 10));
​
// 2. 向cpp传递函数示例
addon.about(function(msg){
    console.log('2.函数回调结果:', msg);
})
​
// 3. 传二维数组值示例
const data = [["aa","bb","cc","dd"],["aaa","bbb","ccc","ddd"]]
console.log('3.Send(js)Array:',  data);
addon.jsToCpp2DArray(data)
​
// 4. 传复杂对象数组值示例
const obj ={ "amount":99, "letters":[["aa","bb","cc","dd"],["aaa","bbb","ccc","ddd"]] }
console.log('4.Send(js)Object:',  obj);
addon.jsToCppObject(obj)

说明:示例1和示例2分别为JS调用C++函数和C++调用JS函数示例,并且通过函数进行互相传值。(cpp-js-integration-demo不再赘述,C++调用JS函数,参照示例2)

C++代码见demo中addon文件夹中,代码过长不在文档中展示。

相关文档

【1】记一次完整 C++ 项目编译成 WebAssembly 的实践

【2】node.js调用C++的一种方法

【3】使用node-addon-api编写c/c++扩展(传递复杂对象)