Node.jsのモジュールをC++で作成する

関連する投稿
Node.js + nobleを使ってMicrobitのセンサー情報を読み取る
Node.jsのモジュールをC++で作成する(その2)

dualshock3の入力情報を独自のC++コードで作成している。Bluetooth通信でMicrobit側の情報はNode.jsとnobleモジュールで何とか成りそうなので、あとはこのdualshock側のC++コードをどうにかNode.jsで利用できれば、連携できそう。

ということで、Node.jsモジュールをC++コードで作成できるようなる必要がある。ただし、このアドオンはバージョンが上がるたびに仕様がコロコロ変わるらしい。最新版のドキュメントは以下にあるので、まずこれをビルドしてみる。

C++ Addons

node-gypというビルドツールが必要なので、インストールする。npmでモジュールをダウンロードするときにプラットフォームごとにビルドが実行されることがよくあるが、この時にモジュールを作成しているツールがこれになる。念のため最新版を入れておいたほうが良い。

> npm install -g node-gyp

とりあえずaddontestというフォルダを作成してその中で作業する。

> mkdir addontest
> cd addontest

必要なのは、C++のソースファイル(hello.cc)とビルドの設定ファイルとなるbinding.gypを用意する必要がある。最近はいろんな形式でモジュール用プログラムを記述できるようだが、情報が多いv8形式で作成する。

よくわからない記述だらけだが、カスタマイズが必要なのは以下の場合、void Method(const FunctionCallbackInfo<Value>& args)だけである。今回Methodという名称で作成したが、なんでも良い。最後にjavascriptからコールするときのfunction名と実際に呼び出される関数の紐づけを、NODE_SET_METHOD(exports, "hello", Method);でやっている。ほかはお約束コードのようなものだと割り切っている。

> vi hello.cc

// hello.cc

#include <node.h>
namespace demo {
  using v8::FunctionCallbackInfo;
  using v8::Isolate;
  using v8::Local;
  using v8::Object;
  using v8::String;
  using v8::Value;

  void Method(const FunctionCallbackInfo<Value>& args) {
      Isolate* isolate = args.GetIsolate();
        args.GetReturnValue().Set(String::NewFromUtf8(isolate, "world"));
  }
  void init(Local<Object> exports) {
      NODE_SET_METHOD(exports, "hello", Method);
  }
  NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

ビルドの設定ファイル。ソースはどれなのか、作成するモジュール名をどうするのかを以下のように記載する。

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

最後にnode-gypを実行する。第2引数の名称でサブフォルダを指定すると、その中にモジュールが作成される。

> node-gyp configure build
gyp info it worked if it ends with ok
gyp info using node-gyp@3.6.2
gyp info using node@8.10.0 | linux | x64
gyp info spawn /usr/bin/python2
gyp info spawn args [ '/usr/local/node-v6.11.4/lib/node_modules/node-gyp/gyp/gyp_main.py',
gyp info spawn args   'binding.gyp',
gyp info spawn args   '-f',
gyp info spawn args   'make',
gyp info spawn args   '-I',
gyp info spawn args   '/home/hoge/work/addontest/modules/build/config.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/usr/local/node-v6.11.4/lib/node_modules/node-gyp/addon.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/home/hoge/.node-gyp/8.10.0/include/node/common.gypi',
gyp info spawn args   '-Dlibrary=shared_library',
gyp info spawn args   '-Dvisibility=default',
gyp info spawn args   '-Dnode_root_dir=/home/hoge/.node-gyp/8.10.0',
gyp info spawn args   '-Dnode_gyp_dir=/usr/local/node-v6.11.4/lib/node_modules/node-gyp',
gyp info spawn args   '-Dnode_lib_file=/home/hoge/.node-gyp/8.10.0/<(target_arch)/node.lib',
gyp info spawn args   '-Dmodule_root_dir=/home/hoge/work/addontest/modules',
gyp info spawn args   '-Dnode_engine=v8',
gyp info spawn args   '--depth=.',
gyp info spawn args   '--no-parallel',
gyp info spawn args   '--generator-output',
gyp info spawn args   'build',
gyp info spawn args   '-Goutput_dir=.' ]
make: ディレクトリ '/home/hoge/work/addontest/modules/build' に入ります
gyp info spawn make
gyp info spawn args [ 'BUILDTYPE=Release', '-C', 'build' ]
  CXX(target) Release/obj.target/addon/hello.o
  SOLINK_MODULE(target) Release/obj.target/addon.node
  COPY Release/addon.node
make: ディレクトリ '/home/hoge/work/addontest/modules/build' から出ます
gyp info ok 
hoge@ubuntu:~/work/addontest/modules$

上記のようにモジュールを作成すると、javascript側では以下のようにコールすることができる。

> vi hello.js

const addon = require('./build/Release/addon');
console.log(addon.hello());

実際に実行した結果が以下になる。

> node hello.js
world