Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9241

【Step-By-Step】Node.jsからC++クラスを利用するための環境構築

$
0
0

はじめに

この記事は、javascriptからC++を呼び出す処理が必要になった時の備忘録です。
node-addon-apiというラッピングライブラリを活用します。
この記事の内容を「マネすれば動く」ように意識して書いています。
Electronなどのデスクトップアプリに応用できます。

事前準備

  • 下記のパッケージは事前にインストールしておいてください
    • npm
    • node.js

応用記事はこちら!

1. Node.js からC++関数への引数、返り値まとめ

2. Node.jsからC++クラス、dllを使う

目次

1. プロジェクトの新規作成

  • 空のフォルダを新規作成します。今回は例として、napi_sampleというフォルダ名にしました。
  • 作成したフォルダに移動し、下記のコマンドを実行し、必要モジュールをインストールします。
terminal
$ npm init -y$ npm install node bindings node-addon-api
  • コマンド実行後、下記のようなフォルダ構成となります。

 カレントディレクトリ
  ├── node_modules
  ├── package-lock.json
  └── package.json

  • さらに、下記のようなpackage.jsonが自動的に作成されます。
package.json
{"name":"doit_myself","version":"1.0.0","description":"","main":"index.js",//<--開始時にこの.jsファイルが読み込まれる"scripts":{"test":"echo \"Error: no test specified\"&& exit 1"},"author":"","license":"ISC","dependencies":{"bindings":"^1.5.0","node":"^15.8.0","node-addon-api":"^3.1.0"}}

index.jsを作成する

カレントディレクトリ
 ├── node_modules
 ├── package-lock.json
 ├── package.json
 └── index.js <-- 新規作成

  • 起動時に読み込まれるindex.jsを追加しましょう。
    • 下記のようなサンプルとします。
index.js
console.log("Hello! node.js");

ターミナルで動作確認する

  • ここまでの環境構築がうまくいっているか確認します。
    • index.jsがあるディレクトリで下記のコマンドを入力してください。
terminal
$ node .>> Hello! node.js

2. Cppファイルを追加する

  • ここからは、jsで利用するためのCppラッパークラスを作成していきます。
    • wrapper.h, wrapper.cc, addon.ccの順に説明します。

カレントディレクトリ
 ├── node_modules
 ├── package-lock.json
 ├── package.json
 ├── index.js
 ├── addon.cc <-- 新規作成
 ├── wrapper.cc <-- 新規作成
 └── wrapper.h <-- 新規作成
※ 拡張子.cc はC++ファイルのことです。本質は .cppと変わりません。

ラッパークラスの作成

  • ネイティブC++をラッピングするクラスを作成します。
    • このクラスの目的は、jsから渡された引数をC++で解釈できる形にし、C++の返り値をjsが利用できる形式に変換して渡すことです。
  • 下記のwrapper.h, wrapper.ccをテンプレとしてお使いください。
wrapper.h
#ifndef WRAPPER
#define WRAPPER
#include <napi.h> // 必要なヘッダ
classWrapper:publicNapi::ObjectWrap<Wrapper>{public:staticNapi::ObjectInit(Napi::Envenv,Napi::Objectexports);staticNapi::ObjectNewInstance(Napi::Envenv,constNapi::CallbackInfo&info);Wrapper(constNapi::CallbackInfo&info);~Wrapper();Napi::ValuegetNum(constNapi::CallbackInfo&info);private:doublem_value;};#endif
wrapper.cc
#include "wrapper.h"
#include <napi.h>
usingnamespaceNapi;// ---------------------------------------------------------- //// ---------------------のり付け部分--------------------------- //// ---------------------------------------------------------- //// new() の定義Napi::ObjectWrapper::NewInstance(Napi::Envenv,constNapi::CallbackInfo&info){Napi::EscapableHandleScopescope(env);// jsからコンストラクタに渡されるArgsは infoに配列として入っているconststd::initializer_list<napi_value>initArgList={info[0]};// ここでWrapper:::Wrapper()が呼ばれるNapi::Objectobj=env.GetInstanceData<Napi::FunctionReference>()->New(initArgList);// gcにメモリ解放されないようにスコープを除外するreturnscope.Escape(napi_value(obj)).ToObject();}// メンバ関数のバインドNapi::ObjectWrapper::Init(Napi::Envenv,Napi::Objectexports){Napi::Functionfunc=DefineClass(env,"Wrapper",{// ここにメンバ関数を登録するInstanceMethod("getNum",&Wrapper::getNum),// InstanceMethod("jsから呼び出す際の関数名", "呼び出したいC++メンバ関数名"),});Napi::FunctionReference*constructor=newNapi::FunctionReference();*constructor=Napi::Persistent(func);env.SetInstanceData(constructor);exports.Set("Wrapper",func);returnexports;}// ---------------------------------------------------------- //// --------------- Wrapperクラスの定義はこれより下 --------------- //// ---------------------------------------------------------- //// コンストラクタWrapper::Wrapper(constNapi::CallbackInfo&info):Napi::ObjectWrap<Wrapper>(info){m_value=0.0;};Wrapper::~Wrapper(){};// メンバ関数Napi::ValueWrapper::getNum(constNapi::CallbackInfo&info){Napi::Envenv=info.Env();returnNapi::Number::New(env,this->m_value);}
  • 補足
    • wrapper.cpp内の関数Napi::Function func = DefineClass()において、自作のC++メンバ関数を登録する必要があります。サンプルコードInstanceMethod("getNum", &Wrapper::getNum),のように、InstanceMethod("jsから呼び出す際の関数名", "呼び出したいC++メンバ関数名")として登録しなければなりません。

jsとの結合用cppファイルを作る

  • 次に、Wrapperクラスをjsモジュールとしてエクスポートするための処理をaddon.ccに記述します。
    • こちらも詳細説明は省略します。テンプレとしてお使いください。
addon.cc
#include <napi.h>
#include "wrapper.h"
#include <iostream>
// jsオブジェクトが初期化された時 new()の呼び出しNapi::ObjectCreateObject(constNapi::CallbackInfo&info){returnWrapper::NewInstance(info.Env(),info);}// js内でexport()が呼び出されたときNapi::ObjectInitAll(Napi::Envenv,Napi::Objectexports){// 関数定義Napi::Objectnew_exports=Napi::Function::New(env,CreateObject);returnWrapper::Init(env,new_exports);}// jsへバインドするためのマクロ// jsで、 export('bindings')('addon')と記述したとき、上記のInitAll()が呼び出されるNODE_API_MODULE(addon,InitAll)

3. Cppファイルをビルドする

  • 作成してたcppをビルドするための設定ファイルを作ります。
    • binding.gyp というファイルです。
    • VisualStudioのprojectのプロパティ設定、CMakeLists.txtと似たような設定をします。

binding.gypの追加

カレントディレクトリ
 ├── node_modules
 ├── package-lock.json
 ├── package.json
 ├── index.js
 ├── addon.cc
 ├── wrapper.cc
 ├── wrapper.h
 └── binding.gyp <-- 新規作成

binding.gyp
{"targets":[{# ↓addon.cc内の NODE_API_MODULE(addon, InitAll) と同名にする
"target_name":"addon","cflags!":["-fno-exceptions"],"cflags_cc!":["-fno-exceptions"],# ↓必要な.ccファイルを全て記述する
"sources":["addon.cc","wrapper.cc"],"include_dirs":["<!@(node -p \"require('node-addon-api').include\")"],"defines":['NAPI_DISABLE_CPP_EXCEPTIONS'],}]}
  • 上記ファイルをコピペいただければ問題ないです。
    • 注意点として sources セクションには、使用する .cc (or .cpp) 拡張子のファイルを全て登録してください。

ビルド実行

  • 下記コマンドを実行し、ビルドしてください。
terminal
$ npm install.>> gyp info ok と表示されればビルド完了

4. JavaScriptからビルドしたCppクラスを使う

  • お待たせしました。最後に index.jsから wrapper.ccのクラスを使ってみましょう。

index.jsの書き換え

カレントディレクトリ
 ├── node_modules
 ├── package-lock.json
 ├── package.json
 ├── index.js <-- 書き換え
 ├── addon.cc
 ├── wrapper.cc
 ├── wrapper.h
 └── binding.gyp

  • index.jsを以下のように書き換えてください。
index.js
// addon.cc内の NODE_API_MODULE(addon, InitAll) が呼ばれるvarWrapper=require('bindings')('addon');// addon.cc内の CreateObject() が呼ばれるvarobj=newWrapper()// wrapper.cc内で登録した getNum()が呼ばれるconsole.log(obj.getNum());

index.jsの実行

ターミナルで次のように実行します。

terminal
$ node .>> 0 と表示されれば成功
>> Wrapper.m_valueの値が表示されています。

お疲れ様でした。
node-addon-api は"お約束ごと"が多いので、
私はこの記事のようなテンプレを作り、使いまわしています。
参考になれば幸いです。

参考リンク

Others

  • コンストラクタに複数の引数を渡す
  • メンバ関数の引数、返り値について
  • 別のC++クラスを利用する

Viewing all articles
Browse latest Browse all 9241

Trending Articles