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

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

系列文章

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

完整代碼

前置準(zhǔn)備

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

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

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

起步

  1. 安裝依賴
$ yarn add -D node-gyp # 就這一個(gè)依賴就夠了
  • 個(gè)人很喜歡安裝到項(xiàng)目里面爱致,而不是 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 會(huì)根據(jù) binding.gypbuild 文件夾下生成當(dāng)前平臺(tái)的 C/C++ 工程 - 第一步執(zhí)行這個(gè)
    image

ps: 下面的命令干的活都交給 VS 咯,不要去折騰命令咯(除非你沒(méi)有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)建配置文件逢防,語(yǔ)法同 js 版本的 json叶沛。等價(jià)于 webpack.config.js
  • targets 下面的每一項(xiàng)都可以理解為一個(gè) node插件,等價(jià)于 webpack 打包 bundle.js
  • target_namerequire([target_name])
  • sources C/C++ 源碼目錄
  • 更多配置參考
  1. 生成目標(biāo)平臺(tái)項(xiàng)目
$ yarn configure
  1. 啟動(dòng) Visual Studio
    image

    image

編寫(xiě)擴(kuò)展

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

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

  napi_get_cb_info
    · **** 這個(gè)可以說(shuō)是最重要的 API 了,再怎么強(qiáng)調(diào)也不為過(guò) ****
    · 用于獲取 js 的入?yún)⒌福蝗缭贾殿?lèi)型寡痰、對(duì)象類(lèi)型,甚至拿到 function 類(lèi)型的指針地址
    · 可以說(shuō)是 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 對(duì)象

  • 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;         // 只接受一個(gè)參數(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 語(yǔ)言類(lèi)型
    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: 編寫(xiě)好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!'));
  • 運(yùn)行
$ node test/hello.js
Hello world!

Boom Shakalaka

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拦坠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子剩岳,更是在濱河造成了極大的恐慌贞滨,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拍棕,死亡現(xiàn)場(chǎng)離奇詭異晓铆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)绰播,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)骄噪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人幅垮,你說(shuō)我怎么就攤上這事腰池。” “怎么了忙芒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵示弓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我呵萨,道長(zhǎng)奏属,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任潮峦,我火速辦了婚禮囱皿,結(jié)果婚禮上勇婴,老公的妹妹穿的比我還像新娘。我一直安慰自己嘱腥,他們只是感情好耕渴,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著齿兔,像睡著了一般橱脸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上分苇,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天添诉,我揣著相機(jī)與錄音,去河邊找鬼医寿。 笑死栏赴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的靖秩。 我是一名探鬼主播须眷,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盆偿!你這毒婦竟也來(lái)了柒爸?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤事扭,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后乐横,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體求橄,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年葡公,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了罐农。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡催什,死狀恐怖涵亏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蒲凶,我是刑警寧澤气筋,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站旋圆,受9級(jí)特大地震影響宠默,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灵巧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一搀矫、第九天 我趴在偏房一處隱蔽的房頂上張望抹沪。 院中可真熱鬧,春花似錦瓤球、人聲如沸融欧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蹬癌。三九已至,卻和暖如春虹茶,著一層夾襖步出監(jiān)牢的瞬間逝薪,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工蝴罪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留董济,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓要门,卻偏偏與公主長(zhǎng)得像虏肾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子欢搜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容