node.js C/C++ addons 入門
node.js のドキュメントを見ていたら C/C++ で簡単に拡張が書けそうだったので試してみた。
ドキュメントに載っている hello.cc をみてみると:
#include <v8.h>
using namespace v8;
extern "C" void
init (Handle<Object> target)
{
HandleScope scope;
target->Set(String::New("hello"), String::New("world"));
}
この extern "C" void init (Handle<Object> target)
というやつが、jsで require("hello")
したときに呼ばれる感じらしい。この関数だけは必ず実装する必要がある。
この中の Handle
や HandleScope
、String
といったものは全部 v8.h で定義された js 操作用のクラス。
Handle は JavaScript でのデータ全般(数値、文字列、オブジェクト、配列)を表すクラスで、init
関数には何も入っていない空のオブジェクト(ここでは target
)が渡される。
この例ではそのオブジェクトにたいして hello
というキーで world
という文字列を登録している。
なので、これを require すると、
$ node
> require("hello")
{ hello: 'world' }
こんな感じのオブジェクトが返るっていうわけです。簡単ですね。
アドオンのビルドには付属の node-waf
というコマンドを使う。これは waf に node.js アドオン用の機能を追加したものなのかな?
この node-waf は wscript という Makefile みたいな設定ファイルを用意してあげる必要がある。ドキュメントに載っているのはこんな感じ:
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 build
とすることで ./build/default
に hello.node
がつくられる。
$ node
> require("./build/default/hello")
とかすればテストできます。
$ node-waf install
で、$NODE_PATH
で指定されたとこに(たぶん)インストールされ、そうすると単純に require("hello")
ができるようになる。
んでもって、C で書きたい! っていう場合は、
srcdir = '.'
blddir = 'build'
VERSION = '0.0.1'
def set_options(opt):
opt.tool_options('compiler_cxx')
opt.tool_options('compiler_cc')
def configure(conf):
conf.check_tool('compiler_cxx')
conf.check_tool('compiler_cc')
conf.check_tool('node_addon')
def build(bld):
c_obj = bld.new_task_gen('cc')
c_obj.name = 'c_obj'
c_obj.target = 'hello'
c_obj.source = 'foo.c bar.c'
obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
obj.target = 'hello'
obj.source = 'hello.cc'
obj.add_objects = 'c_obj'
こんな感じの wscript を書けばいい模様。とはいえ、v8
自体が C++ 製なので、JavaScript とつなぐところは C++ が必要になる。
require 時になんかエスクポートしたりとかもっと良い感じのモジュールにするには
この辺読めば良さそうですね。
あと、C++ 側は、v8.h、node.h、を中心に include/node にあるヘッダファイルを見るといろいろわかりそう。IOやTimerをつかうときには libev の使い方も知っておく必要がある。 node.js には libev が組み込まれていて、EV_DEFAULT ループを本体がうごかしているから、そこにたいしておもむろに ev_io_start
とかしてあげるだけで拡張内で非同期IO使えます。
あとはいろんな拡張を参考に。Google コード検索で 「compiler_cxx node_addon」というので wscript を検索すると node.js 拡張だけが良い感じで引っかかってくるのでおすすめ。