Node.js v0.11.11 マニュアル & ドキュメンテーション


Addons#

アドオンは動的に共有オブジェクトをリンクします。 それらは、C や C++ のライブラリに接合点を提供します。 API はいくつかのライブラリの知識が必要で、(現時点では) かなり複雑です。

  • V8 JavaScript は C++ のライブラリです。 JavaScript のオブジェクト作成や関数呼び出し等のインタフェースに使用されます。 ドキュメントは主に、v8.h のヘッダファイル (Node のソースツリーの中の deps/v8/include/v8.h) に記されていますが、 オンライン で参照することもできます。
  • libuv は C のイベントループライブラリです。 ファイル記述子が読み取り可能になるのを待つとき、タイマーを待つとき、 シグナルを受信するのを待つときはいつでも、 libv のインタフェースが必要になります。 つまり、何らかの I/O 処理をすると必ず libuv を使う必要があるということです。
  • Node の内部ライブラリ。 もっとも重要なのは node::ObjectWrap クラスで、 このクラスから派生させることが多くなるでしょう。
  • その他。どのような物が利用できるかは deps/ 以下を参照してさい。

Node は全ての依存ライブラリを実行ファイルに静的にコンパイルします。 モジュールのコンパイル時には、それらのリンクについて一切気にする必要は有りません。

以下のサンプルの全ては ダウンロード から利用することができ、独自のアドオンの出発点になるでしょう。

Hello world#

では、 以下の JavaScript コードと同じ様に動作する小さなアドオンを C++ で作成してみましょう。

module.exports.hello = function() { return 'world'; };

最初に hello.cc というファイルを作成します:

// hello.cc
#include <node.h>

using namespace v8;

void Method(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = Isolate::GetCurrent();
  HandleScope scope(isolate);
  args.GetReturnValue().Set(String::NewFromUtf8(isolate, "world"));
}

void init(Handle<Object> exports) {
  NODE_SET_METHOD(exports, "hello", Method);
}

NODE_MODULE(addon, init)

全ての Node アドオンは初期化関数をエクスポートしなければならないことに 注意してください。

void Initialize (Handle<Object> exports);
NODE_MODULE(module_name, Initialize)

NODE_MODULE は関数ではないので、その後にセミコロンを付けてはいけません (node.h を参照してください)。

module_name は最終的なバイナリのファイル名 (拡張子 .node を除く) とマッチする必要があります。

このソースコードは、addon.node というバイナリアドオンとしてビルドされる必要が有ります。 そのために binding.gyp と呼ばれる、あなたのモジュールをビルドするための 構成を JSON 的なフォーマットで記述したファイルを作成します。 このファイルは node-gyp によってコンパイルされます。

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

次のステップは現在のプラットフォームに適したプロジェクトビルドファイルを 生成することです。 node-gyp configure を使います。

これで、Makefile (Unix プラットフォームの場合)、または vcxproj ファイル (Windows の場合) が build/ ディレクトリに作られます。 次に node-gyp build コマンドを起動します。

これでコンパイルされた .node バインディングファイルが作成されます! コンパイルされたバインディングファイルは build/Release/ にあります。

ビルドされた hello.node モジュールを require で指定することにより、 このバイナリアドオンを Node プロジェクトの hello.js から利用することが 可能になります。

// hello.js
var addon = require('./build/Release/addon');

console.log(addon.hello()); // 'world'

さらに詳しい情報については下記のパターンか、

https://github.com/arturadib/node-qt を実際のプロダクトにおける 例として参照してください。

Addon patterns#

以下は初心者に役立つアドオンのパターンです。 v8 の様々な API についてはオンラインの v8 reference が、 そして ハンドルやスコープ、関数テンプレートなどいくつかの概念については v8 の Embedder's Guide が 役に立つでしょう。

このサンプルを利用できるようにするには、node-gyp を使用して コンパイルする必要があります。 以下の binding.gyp ファイルを作成します。

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

一つ以上の .cc ファイルがある場合は、単純に sources 配列にファイル名を 加えるだけです。例:

"sources": ["addon.cc", "myexample.cc"]

これで binding.gyp の準備ができました。 アドオンをコンフィギュアおよびビルドするには:

$ node-gyp configure build

Function arguments#

以下のパターンは JavaScript から呼び出された関数で引数を読み出したり、 結果を返す方法を示します。これは 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;
  }

  Local<Number> num = Number::New(args[0]->NumberValue() +
      args[1]->NumberValue());

  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) );

Callbacks#

JavaScript の関数を C++ の関数に渡してそこから呼び出すことができます。 これは 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(Context::GetCurrent()->Global(), argc, argv);
}

void Init(Handle<Object> exports, Handle<Object> module) {
  NODE_SET_METHOD(module, "exports", RunCallback);
}

NODE_MODULE(addon, Init)

この例は二つの引数を取る形式の Init() を使用して、第2引数で完全な module オブジェクトを受け取っていることに注意してください。 これは、exports のプロパティとして関数を加える代わりに、アドオンが 一つの関数で exports を完全に上書きすることを可能にします。

以下の JavaScript コード片でテストすることができます。

// test.js
var addon = require('./build/Release/addon');

addon(function(msg){
  console.log(msg); // 'hello world'
});

Object factory#

C++ 関数の中から新しいオブジェクトを作成して返すことができます。 以下の addon.cc のパターンでは、createObject() に渡された文字列を 反映する msg プロパティを持ったオブジェクトを返します。

// 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();
  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'

Function factory#

このパターンは C++ 関数をラップした JavaScript 関数を作成して返す方法を 示します。

// 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(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'

Wrapping C++ objects#

ここでは、 C++ オブジェクト/クラスをラップし、JavaScript から new 演算子を使って インスタンス化できる MyObject を作成します。 最初にメインモジュール 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)

次に、node::ObjectWrap を継承したラッパーを 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(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 に実装します。 ここでは、コンストラクタに渡された値に加算する 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(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.This());
  obj->value_ += 1;

  args.GetReturnValue().Set(Number::New(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

Factory of wrapped objects#

JavaScript の new 演算子で明示的にインスタンス化することなく、 ネイティブオブジェクトを作成できるようにしたい場合に便利です。例:

var obj = addon.createObject();
// instead of:
// var obj = new addon.Object();

createObjectaddon.cc` に登録しましょう:

// 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 にオブジェクトを生成する static メソッド 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

実装は前述の 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(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.This());
  obj->value_ += 1;

  args.GetReturnValue().Set(Number::New(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

Passing wrapped objects around#

C++ オブジェクトをラップして返すことに加えて、Node が提供するヘルパ関数 node::ObjectWrap::Unwrap を使用してアンラップすることもできます。 以下の addon.cc では、二つの MyObject オブジェクトを受け取る add() 関数を導入します:

// 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(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 の実装はこれまでと同様です:

// 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(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