NodeJs C/C++ 擴(kuò)展入門

1、注:此教程默認(rèn)你具備一定的 C/C++ 基礎(chǔ)語法知識
2、如:指針、結(jié)構(gòu)體等
3、本教程以 windows 操作系統(tǒng)為例,假設(shè)你會簡單的使用 Visual Studio

系列文章

  1. C/C++ Addons 入門 Hello world!
  2. C/C++ Addons 對象參數(shù)及回調(diào)函數(shù)
  3. C/C++ Addons 非阻塞多線程回調(diào)
  4. C/C++ Addons windows 下 .dll 動態(tài)鏈接庫 實用篇

完整代碼

前置準(zhǔn)備

工具
  • node-gyp
    • Google 出品的跨平臺構(gòu)建工具,初衷是用來打包 chromium 的
    • gypgenerate your package,將你的 C/++ 代碼編譯成 node.js 可識別的文件
    • 類似 webpackvue、jsx 等方言編譯成為瀏覽器可識別文件
    • 也可以用 cMake.js 做同樣的事情
    • .node 文件在 windows 平臺下既 .dll 在 *nix 平臺下 .so 文件,.node 的尾綴只是看起來 自然些 (20.0404 - 深入淺出node.js)
  • python
  • Visual Studio2017
    • C/C++ 在 windows 下依賴 VS - 個人推薦
    • 官方給出了木有 VS 的方案,windows-build-tools 但這沒法用最重要語法提示、報錯功能 - 不推薦
姿勢

目前一共有三種方式可以編寫 node.js 擴(kuò)展,本文以官方推薦的 N-API 為例

  • N-API
    • node.js 由官方維護(hù)的 node.js 擴(kuò)展 api
    • 純 C 語法不依賴 node.js 版本,node.js 更新后基于 N-API 寫的插件照樣用,官方的解釋是底層調(diào)用的 node.js 穩(wěn)定版的二進(jìn)制接口
  • node-addon-api
    • N-API 的 C++ 包裝版本(有對象,更美好??),目前 (Release 2.0.0) 并未完全的包裝 N-API 的所有 api
  • nan
    • N-API 沒出來之前主要的插件開發(fā)方式
    • “雖然”依賴 node.js 版本,但是維護(hù)團(tuán)隊很賣力,幫忙做好了每個版本的編譯所以就 不依賴 node.js 版本了 ??
  • 原生 C/C++
    • 極度復(fù)雜,需要用一些 v8 api、源碼
    • 依賴 node.js 版本,所以很難用 ??

起步

  1. 安裝依賴
$ yarn add -D node-gyp # 就這一個依賴就夠了
  • 個人很喜歡安裝到項目里面,而不是 yarn add -g node-gyp
  • package.js 配置 scripts
{
  "scripts": {
    "configure": "node-gyp configure",
    "build": "node-gyp build",
    "clean": "node-gyp clean",
    "rebuild": "node-gyp rebuild"
  }
}
  • configure 會根據(jù) binding.gypbuild 文件夾下生成當(dāng)前平臺的 C/C++ 工程 - 第一步執(zhí)行這個
    image

ps: 下面的命令干的活都交給 VS 咯,不要去折騰命令咯(除非你沒有VS)

  • build(可選) 如果你不想用 VS,只是編譯已有的 C/C++ 程序,那么這條命令可以代替 VS 幫你構(gòu)建 - 需要 windows-build-tools
  • clean(可選) 把 build 目錄清除
  • rebuild(可選) 依次執(zhí)行 clean、configure、build 三條命令
  1. 新建 binding.gyp
{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "src/hello.c" ],
    }
  ]
}
  • 構(gòu)建配置文件,語法同 js 版本的 json。等價于 webpack.config.js
  • targets 下面的每一項都可以理解為一個 node插件,等價于 webpack 打包 bundle.js
  • target_namerequire([target_name])
  • sources C/C++ 源碼目錄
  • 更多配置參考
  1. 生成目標(biāo)平臺項目
$ yarn configure
  1. 啟動 Visual Studio
    image

    image

編寫擴(kuò)展

  • 一些 API 說明
  napi_status 枚舉
    · 調(diào)用任意 N-API 后返回值類型
  napi_extended_error_info 結(jié)構(gòu)體
    · 表示調(diào)用 N-API 后發(fā)生的錯誤對象
  napi_env 結(jié)構(gòu)體
    · 告訴 N-API 當(dāng)前執(zhí)行上下文,在調(diào)用 Addons 時自動(Init)傳入
    · 調(diào)用任意多個、或嵌套 N-API 時候需要一直傳遞下去,不允許重用
  napi_callback_info 
    · 用于 Addons 獲取 js 調(diào)用時候傳入的上下文信息,如參數(shù)
  napi_value 不透明指針
    · N-API 提供的在 C、js 中間的一種數(shù)據(jù)類型
    · 任意的 js 數(shù)據(jù)類型都可以賦值給 napi_value,然后通過 N-API 提供的方法再把 napi_value 轉(zhuǎn)成 C 語言的類型,反之亦然
  napi_threadsafe_function 不透明指針
    · 代表一個 js 的 function,在多線程模式下通過 napi_call_threadsafe_function 調(diào)用實現(xiàn)異步 ??

  # 函數(shù)
  napi_create_string_utf8
    · 創(chuàng)建 napi 類型的 string
    · 相當(dāng)于 const str = 'Hello world!'
  napi_get_property
    · 從 napi 類型的對象中取值
    · 相當(dāng)于對 json = { name: 'anan', age: 29 } 取值: console.log(json.name, json.age)

  napi_get_cb_info
    · **** 這個可以說是最重要的 API 了,再怎么強(qiáng)調(diào)也不為過 ****
    · 用于獲取 js 的入?yún)?;如原始值類型、對象類型,甚至拿?function 類型的指針地址
    · 可以說是 js 和 N-NAP 之前的橋梁

  napi_call_function
    · Addons 調(diào)用 js 回調(diào)
  napi_create_function
    · 創(chuàng)建 js 函數(shù)
  napi_get_global
    · 在 Addons 中獲取 js 的 global 對象

  • C/C++ src/hello.c
#include <stdio.h>
#include <node_api.h>
#include <string.h>

napi_value Hello(napi_env env, napi_callback_info info) {
    size_t argc = 1;         // 只接受一個參數(shù)
    napi_value argv;         // 接收到的參數(shù)
    char n[40];
    char hello[90] = "Hello ";
    napi_value result;
    napi_get_cb_info(env, info, &argc, &argv, NULL, NULL);                     // 獲取接收參數(shù)
    napi_get_value_string_utf8(env, argv, n, sizeof(n), NULL);                 // 將接收到的參數(shù)轉(zhuǎn)換為 C 語言類型
    napi_create_string_utf8(env, strcat(hello, n), NAPI_AUTO_LENGTH, &result); // 拼接字符串

    return result;
}

napi_value Init(napi_env env, napi_value exports) {
    // 描述 hello 屬性
    napi_property_descriptor desc = {
        "hello",
        NULL,
        Hello,
        NULL,
        NULL,
        NULL,
        napi_default,
        NULL };
    // 將 hello 掛載到 exports 上面 === require('hello.node').hello;
    napi_define_properties(env, exports, 1, &desc);

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

ps: 編寫好C代碼后,Ctrl+Shift+b (VS編譯快捷鍵)

  • javascript test/hello.js
// const addon = require('./build/Debug/hello.node'); // 如果 VS 編譯模式是 Debug
const addon = require('./build/Release/hello.node'); // 如果 VS 編譯模式是 Release

console.log(addon.hello('world!'));
  • 運行
$ node test/hello.js
Hello world!

Boom Shakalaka

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容