?
This document uses PHP Chinese website manual Release
插件 Addons 是動態(tài)鏈接的共享對象。他提供了 C/C++ 類庫能力。這些API比較復(fù)雜,他包以下幾個類庫:
V8 JavaScript, C++ 類庫。用來和 JavaScript 交互,比如創(chuàng)建對象,調(diào)用函數(shù)等等。
在 v8.h
頭文件中 (目錄地址deps/v8/include/v8.h
),線上地址libuv,C 事件循環(huán)庫。
等待文件描述符變?yōu)榭勺x,等待定時器,等待信號時,會和 libuv 打交道?;蛘哒f,如果你需要和 I/O 打交道,就會用到 libuv。
內(nèi)部 Node 類庫。 其中最重要的類 node::ObjectWrap
,你會經(jīng)常派生自它。
其他的參見 deps/
。
Node 已經(jīng)將所有的依賴編譯成可以執(zhí)行文件,所以你不必當(dāng)心這些類庫的鏈接問題。
以下所有例子可以在node-gyp編譯。
{ "targets": [ { "target_name": "addon", "sources": [ "hello.cc" ] } ] }
下一步創(chuàng)建一個 node-gyp configure
工程,在平臺上生成這些文件。
創(chuàng)建后,在build/
文件夾里擁有一個 Makefile
(Unix 系統(tǒng)) 文件或者 vcxproj
文件
(Windows 系統(tǒng))。 接著調(diào)用 node-gyp build
命令編譯,生成 .node
文件。 這些文件在 build/Release/
目錄里。
現(xiàn)在,你能在 Node 工程中使用這些 2 進制擴展插件,在 hello.js
中聲明require
之前編譯的hello.node
:
// hello.js var addon = require('./build/Release/addon'); console.log(addon.hello()); // 'world'
更多的信息請參考v8 reference 文檔里包含 v8 的各種接口,<a rel="nofollow" href="http://code.google.com/apis/v8/embed.html"">Embedder's Guide這個文檔包含各種說明,比如 handles, scopes, function templates, 等等。
在使用這些例子前,你需要先用 node-gyp
編譯。
創(chuàng)建binding.gyp
文件:
{ "targets": [ { "target_name": "addon", "sources": [ "addon.cc" ] } ] }
將文件名加入到 sources
數(shù)組里就可以使用多個 .cc
文件,例如 :
"sources": ["addon.cc", "myexample.cc"]
準備好 binding.gyp
文件后, 你就能配置并編譯插件:
$ node-gyp configure build
從以下模式中解釋了如何從 JavaScript 函數(shù)中讀取參數(shù),并返回結(jié)果。僅需要一個addon.cc
文件:
// addon.cc #include <node.h> using namespace v8; void Add(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); if (args.Length() < 2) { isolate->ThrowException(Exception::TypeError( String::NewFromUtf8(isolate, "Wrong number of arguments"))); return; } if (!args[0]->IsNumber() || !args[1]->IsNumber()) { isolate->ThrowException(Exception::TypeError( String::NewFromUtf8(isolate, "Wrong arguments"))); return; } double value = args[0]->NumberValue() + args[1]->NumberValue(); Local<Number> num = Number::New(isolate, value); args.GetReturnValue().Set(num); } void Init(Handle<Object> exports) { NODE_SET_METHOD(exports, "add", Add); } NODE_MODULE(addon, Init)
可以用以下的 JavaScript 代碼片段測試:
// test.js var addon = require('./build/Release/addon'); console.log( 'This should be eight:', addon.add(3,5) );
你也能傳 JavaScript 函數(shù)給 C++ 函數(shù),并執(zhí)行它。 在 addon.cc
中:
// addon.cc #include <node.h> using namespace v8; void RunCallback(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); Local<Function> cb = Local<Function>::Cast(args[0]); const unsigned argc = 1; Local<Value> argv[argc] = { String::NewFromUtf8(isolate, "hello world") }; cb->Call(isolate->GetCurrentContext()->Global(), argc, argv); } void Init(Handle<Object> exports, Handle<Object> module) { NODE_SET_METHOD(module, "exports", RunCallback); } NODE_MODULE(addon, Init)
注意,這個例子中使用了 Init()
里的 2 個參數(shù),module
對象是第二個參數(shù)。它允許 addon 使用一個函數(shù)完全重寫 exports
。
可以用以下的代碼來測試:
// test.js var addon = require('./build/Release/addon'); addon(function(msg){ console.log(msg); // 'hello world' });
在 addon.cc
模式里,你能用 C++ 函數(shù)創(chuàng)建并返回一個新的對象,這個對象所包含的 msg
屬性是由createObject()
函數(shù)傳入:
// addon.cc #include <node.h> using namespace v8; void CreateObject(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); Local<Object> obj = Object::New(isolate); obj->Set(String::NewFromUtf8(isolate, "msg"), args[0]->ToString()); args.GetReturnValue().Set(obj); } void Init(Handle<Object> exports, Handle<Object> module) { NODE_SET_METHOD(module, "exports", CreateObject); } NODE_MODULE(addon, Init)
使用 JavaScript 測試:
// test.js var addon = require('./build/Release/addon'); var obj1 = addon('hello'); var obj2 = addon('world'); console.log(obj1.msg+' '+obj2.msg); // 'hello world'
這個模式里展示了如何創(chuàng)建并返回一個 JavaScript 函數(shù),它是由 C++ 函數(shù)包裝的 :
// addon.cc #include <node.h> using namespace v8; void MyFunction(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); args.GetReturnValue().Set(String::NewFromUtf8(isolate, "hello world")); } void CreateFunction(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, MyFunction); Local<Function> fn = tpl->GetFunction(); // omit this to make it anonymous fn->SetName(String::NewFromUtf8(isolate, "theFunction")); args.GetReturnValue().Set(fn); } void Init(Handle<Object> exports, Handle<Object> module) { NODE_SET_METHOD(module, "exports", CreateFunction); } NODE_MODULE(addon, Init)
測試:
// test.js var addon = require('./build/Release/addon'); var fn = addon(); console.log(fn()); // 'hello world'
以下會創(chuàng)建一個 C++對象的包裝MyObject
,這樣 他就能再 JavaScript 中用 new
實例化。首先在addon.cc
中準備主要模塊:
// addon.cc #include <node.h> #include "myobject.h" using namespace v8; void InitAll(Handle<Object> exports) { MyObject::Init(exports); } NODE_MODULE(addon, InitAll)
接著在 myobject.h
創(chuàng)建包裝,它繼承自 node::ObjectWrap
:
// myobject.h #ifndef MYOBJECT_H #define MYOBJECT_H #include <node.h> #include <node_object_wrap.h> class MyObject : public node::ObjectWrap { public: static void Init(v8::Handle<v8::Object> exports); private: explicit MyObject(double value = 0); ~MyObject(); static void New(const v8::FunctionCallbackInfo<v8::Value>& args); static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args); static v8::Persistent<v8::Function> constructor; double value_; }; #endif
在 myobject.cc
中實現(xiàn)各種暴露的方法,通過給構(gòu)造函數(shù)添加 prototype 屬性來暴露 plusOne
方法:
// myobject.cc #include "myobject.h" using namespace v8; Persistent<Function> MyObject::constructor; MyObject::MyObject(double value) : value_(value) { } MyObject::~MyObject() { } void MyObject::Init(Handle<Object> exports) { Isolate* isolate = Isolate::GetCurrent(); // Prepare constructor template Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New); tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject")); tpl->InstanceTemplate()->SetInternalFieldCount(1); // Prototype NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne); constructor.Reset(isolate, tpl->GetFunction()); exports->Set(String::NewFromUtf8(isolate, "MyObject"), tpl->GetFunction()); } void MyObject::New(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); if (args.IsConstructCall()) { // Invoked as constructor: `new MyObject(...)` double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); MyObject* obj = new MyObject(value); obj->Wrap(args.This()); args.GetReturnValue().Set(args.This()); } else { // Invoked as plain function `MyObject(...)`, turn into construct call. const int argc = 1; Local<Value> argv[argc] = { args[0] }; Local<Function> cons = Local<Function>::New(isolate, constructor); args.GetReturnValue().Set(cons->NewInstance(argc, argv)); } } void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder()); obj->value_ += 1; args.GetReturnValue().Set(Number::New(isolate, obj->value_)); }
測試:
// test.js var addon = require('./build/Release/addon'); var obj = new addon.MyObject(10); console.log( obj.plusOne() ); // 11 console.log( obj.plusOne() ); // 12 console.log( obj.plusOne() ); // 13
當(dāng)你想創(chuàng)建本地對象,又不想在 JavaScript 中嚴格的使用 new
初始化的時候,以下方法非常實用。
var obj = addon.createObject(); // instead of: // var obj = new addon.Object();
在 addon.cc
中注冊 createObject
方法:
// addon.cc #include <node.h> #include "myobject.h" using namespace v8; void CreateObject(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); MyObject::NewInstance(args); } void InitAll(Handle<Object> exports, Handle<Object> module) { MyObject::Init(); NODE_SET_METHOD(module, "exports", CreateObject); } NODE_MODULE(addon, InitAll)
在 myobject.h
中有靜態(tài)方法 NewInstance
,他能實例化對象 (它就像 JavaScript 的 new
):
// myobject.h #ifndef MYOBJECT_H #define MYOBJECT_H #include <node.h> #include <node_object_wrap.h> class MyObject : public node::ObjectWrap { public: static void Init(); static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args); private: explicit MyObject(double value = 0); ~MyObject(); static void New(const v8::FunctionCallbackInfo<v8::Value>& args); static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args); static v8::Persistent<v8::Function> constructor; double value_; }; #endif
這個實現(xiàn)方法和 myobject.cc
類似:
// myobject.cc #include <node.h> #include "myobject.h" using namespace v8; Persistent<Function> MyObject::constructor; MyObject::MyObject(double value) : value_(value) { } MyObject::~MyObject() { } void MyObject::Init() { Isolate* isolate = Isolate::GetCurrent(); // Prepare constructor template Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New); tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject")); tpl->InstanceTemplate()->SetInternalFieldCount(1); // Prototype NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne); constructor.Reset(isolate, tpl->GetFunction()); } void MyObject::New(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); if (args.IsConstructCall()) { // Invoked as constructor: `new MyObject(...)` double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); MyObject* obj = new MyObject(value); obj->Wrap(args.This()); args.GetReturnValue().Set(args.This()); } else { // Invoked as plain function `MyObject(...)`, turn into construct call. const int argc = 1; Local<Value> argv[argc] = { args[0] }; Local<Function> cons = Local<Function>::New(isolate, constructor); args.GetReturnValue().Set(cons->NewInstance(argc, argv)); } } void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); const unsigned argc = 1; Handle<Value> argv[argc] = { args[0] }; Local<Function> cons = Local<Function>::New(isolate, constructor); Local<Object> instance = cons->NewInstance(argc, argv); args.GetReturnValue().Set(instance); } void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder()); obj->value_ += 1; args.GetReturnValue().Set(Number::New(isolate, obj->value_)); }
測試:
// test.js var createObject = require('./build/Release/addon'); var obj = createObject(10); console.log( obj.plusOne() ); // 11 console.log( obj.plusOne() ); // 12 console.log( obj.plusOne() ); // 13 var obj2 = createObject(20); console.log( obj2.plusOne() ); // 21 console.log( obj2.plusOne() ); // 22 console.log( obj2.plusOne() ); // 23
除了包裝并返回 C++ 對象,你可以使用 Node 的 node::ObjectWrap::Unwrap
幫助函數(shù)來解包。在下面的 addon.cc
中,我們介紹了一個 add()
函數(shù),它能獲取2個 MyObject
對象:
// addon.cc #include <node.h> #include <node_object_wrap.h> #include "myobject.h" using namespace v8; void CreateObject(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); MyObject::NewInstance(args); } void Add(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>( args[0]->ToObject()); MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>( args[1]->ToObject()); double sum = obj1->value() + obj2->value(); args.GetReturnValue().Set(Number::New(isolate, sum)); } void InitAll(Handle<Object> exports) { MyObject::Init(); NODE_SET_METHOD(exports, "createObject", CreateObject); NODE_SET_METHOD(exports, "add", Add); } NODE_MODULE(addon, InitAll)
介紹 myobject.h
里的一個公開方法,它能在解包后使用私有變量:
// myobject.h #ifndef MYOBJECT_H #define MYOBJECT_H #include <node.h> #include <node_object_wrap.h> class MyObject : public node::ObjectWrap { public: static void Init(); static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args); inline double value() const { return value_; } private: explicit MyObject(double value = 0); ~MyObject(); static void New(const v8::FunctionCallbackInfo<v8::Value>& args); static v8::Persistent<v8::Function> constructor; double value_; }; #endif
myobject.cc
的實現(xiàn)方法和之前的類似:
// myobject.cc #include <node.h> #include "myobject.h" using namespace v8; Persistent<Function> MyObject::constructor; MyObject::MyObject(double value) : value_(value) { } MyObject::~MyObject() { } void MyObject::Init() { Isolate* isolate = Isolate::GetCurrent(); // Prepare constructor template Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New); tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject")); tpl->InstanceTemplate()->SetInternalFieldCount(1); constructor.Reset(isolate, tpl->GetFunction()); } void MyObject::New(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); if (args.IsConstructCall()) { // Invoked as constructor: `new MyObject(...)` double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); MyObject* obj = new MyObject(value); obj->Wrap(args.This()); args.GetReturnValue().Set(args.This()); } else { // Invoked as plain function `MyObject(...)`, turn into construct call. const int argc = 1; Local<Value> argv[argc] = { args[0] }; Local<Function> cons = Local<Function>::New(isolate, constructor); args.GetReturnValue().Set(cons->NewInstance(argc, argv)); } } void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); const unsigned argc = 1; Handle<Value> argv[argc] = { args[0] }; Local<Function> cons = Local<Function>::New(isolate, constructor); Local<Object> instance = cons->NewInstance(argc, argv); args.GetReturnValue().Set(instance); }
測試:
// test.js var addon = require('./build/Release/addon'); var obj1 = addon.createObject(10); var obj2 = addon.createObject(20); var result = addon.add(obj1, obj2); console.log(result); // 30