🥃

Node.js + TypeScript からC++のライブラリを呼び出す

2021/12/10に公開

目的

  • Node.jsからC++のライブラリをコールしたい
  • node-gypを使えばできるっぽいが、どのサンプルも動かない・・・(当方intel Mac / Big Sur)
  • v8のAPIが色々変更してるっぽい。最新環境で動作確認できるサンプルが欲しい

成果物

とりあえず作ってみたサンプル一式は以下です。
https://github.com/swmokyun/sample-node-gyp

環境

  • intel Mac
  • macOS Big Sur (11.6)
  • Node.js 14

Cまわりのセットアップ

以下を参考にCをビルドできる環境を作っておく
https://github.com/nodejs/node-gyp#installation

nodeまわりのセットアップ

  • Node.js 14 を動作できるようにする。自分はnodebrewを使って切り替えています。
  • 以下をyarnで追加
     "@types/node": "^16.11.12",
     "node-gyp": "^8.4.1",
     "ts-node-dev": "^1.1.8",
     "typescript": "^4.5.3"

Cのサンプルコードとgypの設定を用意

binding.gyp
{
    "targets": [
        {
            "target_name": "native_ext",
            "sources": ["native_ext.cc"]
        }
    ]
}
native_ext.cc
#include <node/v8.h> // 自分の環境だとこのパスじゃないと動かなかった
#include <node/node.h>

using namespace node;
using namespace v8;

void Method(const v8::FunctionCallbackInfo<v8::Value> &args)
{
    v8::Isolate *isolate = args.GetIsolate();
    v8::HandleScope scope(isolate);
    v8::Local<v8::String> result;
    v8::MaybeLocal<v8::String> temp = String::NewFromUtf8(isolate, "Hello World");
    temp.ToLocal(&result);
    args.GetReturnValue().Set(result);
}

void Method2(const v8::FunctionCallbackInfo<v8::Value> &args)
{
    if (args.Length() != 2)
    {
        args.GetReturnValue().Set(-1);
        return;
    }

    v8::Isolate *isolate = args.GetIsolate();
    v8::HandleScope scope(isolate);
    v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();

    double p1 = args[0]->NumberValue(context).FromJust();
    double p2 = args[1]->NumberValue(context).FromJust();
    args.GetReturnValue().Set(p1 + p2);
}

void init(v8::Local<v8::Object> target)
{
    NODE_SET_METHOD(target, "hello", Method);
    NODE_SET_METHOD(target, "add", Method2);
}

NODE_MODULE(binding, init);

ビルド

npx node-gyp configure
npx node-gyp build

これで build/Release/native_ext.node が作成される

呼び出し側

上記で作成したnative_ext.nodeを呼び出す

test.ts
var addon = require("./build/Release/native_ext.node")

const ret: string = addon.hello()
console.log(ret) // Hello World!

const ret2_1: number = addon.add()
console.log(ret2_1) // -1

const ret2_2: number = addon.add(1.1, 1.2)
console.log(ret2_2) // 2.3

実行

$ npx ts-node-dev test.ts 
[INFO] 10:57:24 ts-node-dev ver. 1.1.8 (using ts-node ver. 9.1.1, typescript ver. 4.5.3)
Hello World
-1
2.3

おおおお、動きました。

補足

以下のようなエラーがでたらNode.jsのバージョンが合っていない。
おとなしくnodebrewなどでNode.jsのバージョンを切り替える。
Node.jsのバージョン一覧はこちら https://nodejs.org/ja/download/releases/

[ERROR] 11:21:01 Error: The module '/xxx/xxx/sample-node-gyp/src/build/Release/native_ext.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 83. This version of Node.js requires
NODE_MODULE_VERSION 93. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).

Discussion