用C/C 實作 Node.js 的模組(一)_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
{
//... 嗷嗷待寫
}
然後我來粗粗解析一下這些東西:
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
{
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
{
HandleScope scope;
return String::New("world");
}
因為當函數回傳時,scope 會被析構,其管理的Handle也都會被回收,所以這個 String 就會變得沒有意義。
所以呢 V8 就想出了個神奇的點子-HandleScope::Close(Handle
於是就有了我們之前的程式碼 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
這是龜腚!函數名稱什麼的無所謂,但是傳入的參數一定是一個 Handle
然後,我們就在這裡面寫上導出的東西了:
void init(Handle
大致的意思是說,為這個 exports 物件添加一個字段叫 hello,所對應的東西是一個函數,而這個函數就是我們親愛的 Hello 函數了。
用偽代碼寫直白點就是:
void init(Handle
大功告成!
(大功告成你妹啊!閉嘴( ‘д‘⊂彡☆))Д´)
真·導出
這才是最後一步,我們最後要申明,這個就是導出的入口,所以我們在程式碼的末尾加上這一行:
NODE_MODULE(hello, init)
納了個尼? !這又是什麼東西?
別著急,這個 NODE_MODULE 是一個宏,它的意思呢就是說我們採用 init 這個初始化函數來把要導出的東西導出到 hello 中。那麼這個 hello 哪來呢?
它來自檔案名稱!對,沒錯,它來自檔案名稱。你並不需要事先申明它,你也不必擔心不能用,總之你的這個最終編譯好的二進位檔案名叫什麼,這裡的 hello 你就填什麼,當然要除去後綴名了。
詳見官方文件。
Note that all Node addons must export an initialization function:
void Initialize (Handle
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 world 的 C 擴展了吧。下一次寫的應該會更深入一點,至於下一次是什麼時候,我也不知道啦其實。
(餵餵餵,擼主怎麼可以這麼不負責!(o゚ロ゚)┌┛Σ(ノ´ω`)ノ

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

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

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

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

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

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

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

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

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