node.js v5.0.0でC++のnative addonを作る（基礎編）

全部追いかけてないですが、io.jsのなんやかんやとか[NAN](https://github.com/nodejs/nan)とかのおかげでnative addonの作り方がだいぶ変わったらしく、そもそもそれ以前の作り方もだいぶ記憶喪失に近いので、改めて公式ドキュメント見ながらまとめてみました。

まとめはじめたら長くなってしまったので、分割して掲載したいと思います。


とりあえずaddonを作る＆動かす
---------

C++のソースコードを用意し、node-gypでコンパイルするという大まかな流れは変わっていないようです。

### node-gyp のインストール

Node.js本体はインストール済みの＆npmが使える状態のものとします。`node-gyp` がコンパイルなどをよしなにやってくれるツールなのでこれをインストールします。

```
$ npm install -g node-gyp
```

インストール先ディレクトリのPermission問題が起きる場合は `sudo` をつけて実行すると良いでしょう。

### 必要なファイルの用意

同一ディレクトリに `hello.cc` , `binding.gyp` , `test.js` を用意します。

- hello.cc (ほぼ公式のパクリ)

```cpp
#include <node.h>

namespace demo {
  void BlueMethod(const v8::FunctionCallbackInfo<v8::Value>& args) {
    v8::Isolate* isolate = args.GetIsolate();
    args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, "Yes, Yes, Yes!"));
  }

  void init(v8::Local<v8::Object> exports) {
    NODE_SET_METHOD(exports, "hello", BlueMethod);
  }

  NODE_MODULE(addon, init)

}  // namespace demo
```

- binding.gyp

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

- test.js

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

console.log(addon.hello());
```

### コンパイル＆実行

コンパイルは node-gyp で実施します。

```
$ node-gyp configure build
```

二回目以降は `build` だけでいいはずです。

```
$ node-gyp build
```

test.jsを使って実行します。

```
$ node test.js 
Yes, Yes, Yes!
```


引数の処理
-----------

数値の合計をだす `sum` という関数をサンプルにします。`const v8::FunctionCallbackInfo<v8::Value>& args` が可変長の引数情報を保持しているので、実行時に都度引数の数や引数のデータ型をチェックする必要があります。

### ソースコード

- hello.cc

```cpp
#include <node.h>

namespace demo {
  // const v8::FunctionCallbackInfo<v8::Value>& args が引数になる
  void Sum(const v8::FunctionCallbackInfo<v8::Value>& args) {
    v8::Isolate* isolate = args.GetIsolate();

    double v = 0;
    for (int i = 0; i < args.Length(); i++) {
      // 実行時まで引数の型がわからないのでチェック
      if (!args[i]->IsNumber()) {
        // 数値以外だったら例外を投げる
        isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate, "Wrong arguments")));
        // そしてreturnで抜ける
        return;
      }
      // 数値データとしてi番目の引数を取り出す
      v += args[i]->NumberValue();
    }

    // JavaScript界の数値データ形式(v8::Number)に変換する
    v8::Local<v8::Value> num = v8::Number::New(isolate, v);
    // 返り値をセットする
    args.GetReturnValue().Set(num);
  }

  void init(v8::Local<v8::Object> exports) {
    NODE_SET_METHOD(exports, "sum", Sum);
  }

  NODE_MODULE(addon, init)

}  // namespace demo
```

- test.js

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

console.log(addon.sum(1, 2, 3));
console.log(addon.sum(1, 2, 3, 4));
```

### 実行

```
$ node test.js     
6
10
```

コールバック関数を引数に持つ関数の処理
--------------

処理が完了した時にコールバック関数に処理を返すタイプの関数の扱い方。ただしこのやり方だと非同期ではなく、コールバック関数が呼ばれるまではNode.js上の他の全ての処理（例えばsetIntervalで回しているものとか）が止まるので注意。

### ソースコード

- hello.cc

```cpp
#include <node.h>

namespace demo {
  // args[0] にコールバック関数が渡される想定
  void RunCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
    v8::Isolate* isolate = args.GetIsolate();

    // 引数の数と型のチェック
    if (args.Length() != 1 || !args[0]->IsFunction()) {
        isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate, "Wrong arguments")));
        return;
      }

    // 関数型にキャストする
    v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(args[0]);
    // コールバックに渡す引数を用意する
    const unsigned argc = 1;
    v8::Local<v8::Value> argv[argc] = {
      v8::String::NewFromUtf8(isolate, "done!")
    };
    // 呼ぶ
    cb->Call(Null(isolate), argc, argv);    
  }

  void init(v8::Local<v8::Object> exports) {
    NODE_SET_METHOD(exports, "run", RunCallback);
  }

  NODE_MODULE(addon, init)

}  // namespace demo
```

- test.js

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

addon.run(function(result) {
  console.log(result);
});
```

### 実行

```
$ node test.js 
done!
```

オブジェクトを生成して返り値にする
-----------

キーと値を格納できるオブジェクト型（Object）を生成して返します。この例では一層だけですが、値の部分に新たなオブジェクトを格納させることで、階層化されたオブジェクトを格納できます。

ちなみにオブジェクト型の v8::Object とは別に v8::Array 型がありますが、これは v8::Object を継承したクラスになっています。

### ソースコード

- hello.cc

```cpp
#include <node.h>

namespace demo {

  // 与えられた文字列引数をオブジェクトに格納する
  void GetObj(const v8::FunctionCallbackInfo<v8::Value>& args) {
    v8::Isolate* isolate = args.GetIsolate();

    // 引数の数と型のチェック
    if (args.Length() != 1 || !args[0]->IsString()) {
      isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate, "Wrong arguments")));
      return;
    }

    // 新しいObjectを作成する
    v8::Local<v8::Object> obj = v8::Object::New(isolate);
    // キーに "blue"、値に渡された引数を指定する
    obj->Set(v8::String::NewFromUtf8(isolate, "blue"),
             args[0]->ToString());
    // 返り値にセットする
    args.GetReturnValue().Set(obj);
  }

  void init(v8::Local<v8::Object> exports) {
    NODE_SET_METHOD(exports, "get_obj", GetObj);
  }

  NODE_MODULE(addon, init)
}  // namespace demo
```

- test.js

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

var obj = addon.get_obj("orange");
console.log(obj);
```

### 実行

```
$ node test.js                  
{ blue: 'orange' }
```



参考文献
-----------

- [Node.js v5.0.0 Documentation Addon](https://nodejs.org/api/addons.html)