首頁 web前端 js教程 用C/C 實作 Node.js 的模組(一)_node.js

用C/C 實作 Node.js 的模組(一)_node.js

May 16, 2016 pm 04:35 PM
c c++ node.js 模組

 N久之前的一個坑-用 Node.js 來重建 NBUT 的 Online Judge,包括評測端也要重構一遍。 (至於何時完成大家就不要關心了,(/‵Д′)/~ ╧╧

  總之我們現在要做的其實簡而言之就是-用C/C 來實作 Node.js 的模組。

準備工作

  工欲善其事,必先~~耍流氓~~利其器。

node-gyp

  首先你需要一個 node-gyp 模組。

  在任何角落,執行:

複製程式碼 程式碼如下:

$ npm install node-gyp -g

   在進行一系列的 blahblah 之後,你就安裝好了。

Python

  然後你需要有個 python 環境。

  自己去官網搞一個來。


注意: 根據 node-gyp 的GitHub顯示,請務必確保你的 python 版本介於 2.5.0 和 3.0.0 之間。
 
編譯環境

  嘛嘛,我就偷懶點不細寫了,還請自己移步到 node-gyp 去看編譯器的需求。並且倒騰好。

入門

  我就拿官網的入門 Hello World說事兒了。

Hello World

  請準備一個 C 文件,例如就叫 ~~sb.cc~~ hello.cc。

  然後我們一步步來,先往裡面搞出頭文件和定義好命名空間:

複製程式碼 程式碼如下:

#include
#include
using namespace v8;

 主要函數

  接下去我們寫一個函數,其回傳值是 Handle

複製程式碼 程式碼如下:

Handle Hello(const Arguments& args)
{
    //... 嗷嗷待寫
}

   然後我來粗粗解析一下這些東西:

Handle

  做人要有節操,我事先申明我是從這裡(@fool)參考的。


V8 裡使用Handle 類型來託管JavaScript 對象,與C 的std::sharedpointer 類似,Handle 類型間的賦值都是直接傳遞對象引用,但不同的是,V8 使用自己的GC 來管理對像生命週期,而不是智能指標常用的引用計數。

JavaScript 類型在 C 中都有對應的自訂類型,如 String 、 Integer 、 Object 、 Date 、 Array 等,嚴格遵守在 JavaScript 中的繼承關係。 C 中使用這些類型時,必須使用 Handle 託管,以使用 GC 來管理它們的生命週期,而不使用原生堆疊和堆疊。
 
  而這個所謂的 Value ,從 V8 引擎的頭檔 v8.h 中的各種繼承關係中可以看出來,其實就是 JavaScript 中各種物件的基底類別。

  在了解了這件事之後,我們大致能明白上面那段函數的申明的意思就是說,我們寫一個 Hello 函數,其返回的是一個不定類型的值。


注意: 我們只能傳回特定的類型,也就是在 Handle 託管下的 String 啊 Integer 啊等等等等。
 
Arguments

  這個就是傳入這個函數的參數了。我們都知道在 Node.js 中,參數個數是亂來的。而這些參數傳進去到 C 的時候,就轉變成了這個 Arguments 類型的物件了。

  具體的用法我們在後面再說,在這裡只需要明白這個是個什麼東西就好。 (為毛要賣關子?因為 Node.js 官方文件中的例子就是分開來講的,我現在只是講第一個 Hello World 的例子而已( ´థ౪థ)σ

添磚加瓦

  接下去我們就開始添磚加瓦了。就最簡單的兩句話:

複製程式碼 程式碼如下:

Handle Hello(const Arguments& args)
{
    HandleScope scope;
    return scope.Close(String::New("world"));
}

   這兩句話是什麼意思呢?大致的意思就是回傳一個 Node.js 中的字串 "world"。

HandleScope

  同參考自這裡。


Handle 的生命週期和 C 智慧指標不同,並不是在 C 語意的 scope 內生存(即{} 包圍的部分),而需要透過 HandleScope 手動指定。 HandleScope 只能分配在堆疊上,HandleScope 物件宣告後,其後建立的 Handle 都由 HandleScope 來管理生命週期,HandleScope 物件析構後,其管理的 Handle 將由 GC 判斷是否回收。
 
  所以呢,我們得在需要管理他的生命週期的時候申明這個 Scope 。好的,那為什麼我們的程式碼不這麼寫呢?

複製程式碼 程式碼如下:

Handle Hello(const Arguments& args)
{
    HandleScope scope;
    return String::New("world");
}

   因為當函數回傳時,scope 會被析構,其管理的Handle也都會被回收,所以這個 String 就會變得沒有意義。

  所以呢 V8 就想出了個神奇的點子-HandleScope::Close(Handle Value) 函數!這個函數的用處就是關閉這個 Scope 並且把裡面的參數轉交給上一個 Scope 管理,也就是進入這個函數前面的 Scope。

  於是就有了我們之前的程式碼 scope.Close(String::New("world"));。

String::New

  這個 String 類別所對應的就是 Node.js 中原生的字串類別。繼承自 Value 類別。與此類似,還有:

 •Array
•Integer
•Boolean
•Object
•Date
•Number
•Function
•...
 
  這些東西有些是繼承自 Value,有些是二次繼承。我們這裡就不多做研究,自己可以看看 V8 的程式碼(至少是頭檔)研究研究或看看這本手冊。

  而這個 New 呢?這裡可以看的。就是新建一個 String 物件。

  至此,這個主要函數我們就解析完畢了。

導出物件

  我們來溫習一下,如果是在 Node.js 裡面寫的話,我們要怎麼導出函數或物件什麼的呢?

複製程式碼 程式碼如下:

exports.hello = function() {}

   那麼,在 C 中我們該如何做到這一步呢?

初始化函數

  首先,我們寫個初始化函數:

複製程式碼 程式碼如下:

void init(Handle exports)
{
    //... 嗷嗷待寫你妹啊! #゚Å゚)⊂彡☆))゚Д゚)・∵
}

   這是龜腚!函數名稱什麼的無所謂,但是傳入的參數一定是一個 Handle,代表我們下面將要在這貨上導出東西。

  然後,我們就在這裡面寫上導出的東西了:

複製程式碼 程式碼如下:

void init(Handle exports)
{
    exports->Set(String::NewSymbol("hello"),
        FunctionTemplate::New(Hello)->GetFunction());
}

   大致的意思是說,為這個 exports 物件添加一個字段叫 hello,所對應的東西是一個函數,而這個函數就是我們親愛的 Hello 函數了。

  用偽代碼寫直白點就是:

複製程式碼 程式碼如下:

void init(Handle exports)
{
    exports.Set("hello", function hello);
}

   大功告成!

  (大功告成你妹啊!閉嘴( ‘д‘⊂彡☆))Д´)

真·導出

  這才是最後一步,我們最後要申明,這個就是導出的入口,所以我們在程式碼的末尾加上這一行:
NODE_MODULE(hello, init)

   納了個尼? !這又是什麼東西?

  別著急,這個 NODE_MODULE 是一個宏,它的意思呢就是說我們採用 init 這個初始化函數來把要導出的東西導出到 hello 中。那麼這個 hello 哪來呢?

  它來自檔案名稱!對,沒錯,它來自檔案名稱。你並不需要事先申明它,你也不必擔心不能用,總之你的這個最終編譯好的二進位檔案名叫什麼,這裡的 hello 你就填什麼,當然要除去後綴名了。

  詳見官方文件。


Note that all Node addons must export an initialization function:

複製程式碼 程式碼如下:

void Initialize (Handle exports);
NODE_MODULE(module_name, Initialize)

 There is no semi-colon after NODE_MODULE as it's not a function (see node.h).

The module_name needs to match the filename of the final binary (minus the .node suffix).
 
編譯 (๑•́ ₃ •̀๑)

  來吧,讓我們一起編譯吧!

  我們再新建一個類似 Makefile 的歸檔檔案吧-binding.gyp。

  並且在裡面加入這樣的程式碼:

複製程式碼 程式碼如下:

{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc" ]
    }
  ]
}

   為什麼這麼寫呢?可以參考 node-gyp 的官方文件。

configure

  在檔案搞好之後,我們要在這個目錄下面執行這個指令了:

複製程式碼 程式碼如下:

$ node-gyp configure

   如果一切正常的話,應該會產生一個 build 的目錄,然後裡面有相關文件,也許是 M$ Visual Studio 的 vcxproj 文件等,也許是 Makefile ,視平台而定。

build

  Makefile 也產生好之後,我們就開始構造編譯了:
$ node-gyp build

   等到一切編譯完成,才算是真正的大功告成了!不信你去看看 build/Release 目錄,下面是不是有一個 hello.node 檔案了?沒錯,這是 C 等下要給 Node.js 撿的肥皂!

搞基吧! Node ヽ(✿゚▽゚)ノ C

  我們在剛才那個目錄下新建一個檔案 jianfeizao.js:

複製程式碼 程式碼如下:

var addon = require("./build/Release/hello");
console.log(addon.hello());

   看到沒!看到沒!出來了出來了! Node.js 和 C 搞基的結果!這個 addon.hello() 就是我們之前在 C 程式碼中寫的 Handle Hello(const Arguments& args) 了,我們現在就已經把它回傳的值給輸出了。

洗洗睡吧,下節更深入

  時間不早了,今天就寫到這裡了,至此為止大家都能搞出最基礎的 Hello world 的 C 擴展了吧。下一次寫的應該會更深入一點,至於下一次是什麼時候,我也不知道啦其實。
   (餵餵餵,擼主怎麼可以這麼不負責!(o゚ロ゚)┌┛Σ(ノ´ω`)ノ

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

C#與C:歷史,進化和未來前景 C#與C:歷史,進化和未來前景 Apr 19, 2025 am 12:07 AM

C#和C 的歷史與演變各有特色,未來前景也不同。 1.C 由BjarneStroustrup在1983年發明,旨在將面向對象編程引入C語言,其演變歷程包括多次標準化,如C 11引入auto關鍵字和lambda表達式,C 20引入概念和協程,未來將專注於性能和系統級編程。 2.C#由微軟在2000年發布,結合C 和Java的優點,其演變注重簡潔性和生產力,如C#2.0引入泛型,C#5.0引入異步編程,未來將專注於開發者的生產力和雲計算。

表演競賽:Golang vs.C 表演競賽:Golang vs.C Apr 16, 2025 am 12:07 AM

Golang和C 在性能競賽中的表現各有優勢:1)Golang適合高並發和快速開發,2)C 提供更高性能和細粒度控制。選擇應基於項目需求和團隊技術棧。

Golang和C:並發與原始速度 Golang和C:並發與原始速度 Apr 21, 2025 am 12:16 AM

Golang在並發性上優於C ,而C 在原始速度上優於Golang。 1)Golang通過goroutine和channel實現高效並發,適合處理大量並發任務。 2)C 通過編譯器優化和標準庫,提供接近硬件的高性能,適合需要極致優化的應用。

vscode怎麼在終端運行程序 vscode怎麼在終端運行程序 Apr 15, 2025 pm 06:42 PM

在 VS Code 中,可以通過以下步驟在終端運行程序:準備代碼和打開集成終端確保代碼目錄與終端工作目錄一致根據編程語言選擇運行命令(如 Python 的 python your_file_name.py)檢查是否成功運行並解決錯誤利用調試器提升調試效率

vscode在哪寫代碼 vscode在哪寫代碼 Apr 15, 2025 pm 09:54 PM

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

Golang和C:性能的權衡 Golang和C:性能的權衡 Apr 17, 2025 am 12:18 AM

Golang和C 在性能上的差異主要體現在內存管理、編譯優化和運行時效率等方面。 1)Golang的垃圾回收機制方便但可能影響性能,2)C 的手動內存管理和編譯器優化在遞歸計算中表現更為高效。

Python與C:學習曲線和易用性 Python與C:學習曲線和易用性 Apr 19, 2025 am 12:20 AM

Python更易學且易用,C 則更強大但複雜。 1.Python語法簡潔,適合初學者,動態類型和自動內存管理使其易用,但可能導致運行時錯誤。 2.C 提供低級控制和高級特性,適合高性能應用,但學習門檻高,需手動管理內存和類型安全。

在 visual studio code 中使用 c 嗎 在 visual studio code 中使用 c 嗎 Apr 15, 2025 pm 08:03 PM

在 VS Code 中編寫 C 語言不僅可行,而且高效優雅。關鍵在於安裝優秀的 C/C 擴展,它提供代碼補全、語法高亮和調試等功能。 VS Code 的調試功能可幫助你快速定位 bug,而 printf 輸出是老式但有效的調試方法。此外,動態內存分配時應檢查返回值並釋放內存以防止內存洩漏,調試這些問題在 VS Code 中很方便。雖然 VS Code 無法直接幫助進行性能優化,但它提供了一個良好的開發環境,便於分析代碼性能。良好的編程習慣、可讀性和可維護性也至關重要。總之,VS Code 是一

See all articles