Node.js のモジュールをC++で書く際のメモ(1)

Node.jsのモジュールをC++で書く際に調べたことをメモ。

ソース

とりあえず練習で書いてみた。

#include <v8.h>
#include <node.h>
#include <iostream>

using namespace v8;

class Hello: node::ObjectWrap {
public:

	static void Init(const Handle<Object> target) {
		HandleScope scope;
		Local < FunctionTemplate > t = FunctionTemplate::New(New);
		t->InstanceTemplate()->SetInternalFieldCount(1);
		NODE_SET_PROTOTYPE_METHOD(t, "say", Say);
		target->Set(String::New("Hello"), t->GetFunction());
	}

private:

	Hello() {
	}

	void Say(const char *str) {
		std::cout << "Hello " << str << "\n";
	}

	static Handle<Value> New(const Arguments& args) {
		HandleScope scope;
		if (!args.IsConstructCall())
			return args.Callee()->NewInstance();
		try {
			(new Hello())->Wrap(args.This());
		} catch (const char *msg) {
			return ThrowException(Exception::Error(String::New(msg)));
		}
		return args.This();
	}

	static Handle<Value> Say(const Arguments& args) {
		HandleScope scope;
		Unwrap<Hello> (args.This())->Say(*String::Utf8Value(args[0]));
		return Undefined();
	}

};

extern "C" void init(Handle<Object> target) {
	Hello::Init(target);
}
ポイント
  • Node.js のモジュールは必ず以下のシグネチャをもつinit関数を備えなければならない
extern 'C' void init (Handle<Object> target)
  • node::ObjectWrapを継承
    • (new Hello())->Wrap(args.This());でC++オブジェクトをJavaScriptオブジェクトでラップ
    • メソッドを呼ぶ際は、 Unwrap (args.This())->Say(*String::Utf8Value(args[0]));のようにする
  • JavaScriptから呼ぶメソッドのシグネチャは以下
static v8::Handle<v8::Value> Foo(const v8::Arguments& args);
//コンストラクタはHello::New
Local < FunctionTemplate > t = FunctionTemplate::New(Hello::New);
//フィールドの数を指定今回は1
t->InstanceTemplate()->SetInternalFieldCount(1);
//Hello::SayがJavaScriptのsayメソッドで呼び出されるように設定
NODE_SET_PROTOTYPE_METHOD(t, "say", Hello::Say);
//JavScript オブジェクトにシンボル"Hello"を設定。これがJavaScriptオブジェクトのプロトタイプになる。
target->Set(String::New("Hello"), t->GetFunction());
ビルド

ビルドにはNode.jsに付属するnode-wafを使用する。
まずは、wscriptを作成する。以下のファイルをソースと同じディレクトリにwscriptとして作成する。

srcdir = '.'
blddir = 'build'
VERSION = '0.0.1'

def set_options(opt): 
    opt.tool_options('compiler_cxx')

def configure(conf):
    conf.check_tool('compiler_cxx')
    conf.check_tool('node_addon')

def build(bld):
    obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
    obj.target = 'hello'
    obj.source = 'hello.cc'

そして、 ビルドを行う。

$ node-waf configure && node-waf

ビルドが成功すれば ./build/default/hello.nodeが作成されているはず。

動作確認

以下のスクリプトで動作を確認。

var Hello = require('./build/default/hello').Hello;
var hello = new Hello();
hello.say('World');

実行。

$ node hello.js
Hello World

とりあえず、今日はここまで。