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

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

$
0
0

はじめに

  • node.jsからC++関数を利用するための記事、第三弾です。
    • 今回は、C++のネイティブクラスをラッピングしたり、dllを利用するための方法をまとめます。
    • この記事では、node-addon-apiを利用しています。

環境構築がまだの方は?

目次

1. ネイティブC++クラスの追加

  • node.jsで利用したいネイティブのCppファイルを追加します。
  • 前回の記事で作成したプロジェクトを以下のように変更します。

カレントディレクトリ
 ├── node_modules
 ├── package-lock.json
 ├── package.json
 ├── index.js
 ├── addon.cc
 ├── binding.gyp <-- 設定変更
 ├── wrapper.cc
 ├── wrapper.h
 └── native_cpp <-- フォルダ追加
     ├── classA.cpp <-- 新規作成
     └── classA.h <-- 新規作成

今回は例として、classAというサンプルクラスを追加します。

classA.hを見る
classA.h
#pragma once
#include <iostream>
#include <string>
// 所持金を登録・表示するクラスです。classClassA{public:ClassA();//コンストラクタ~ClassA();// デストラクタ// メンバ関数voidset_name(std::stringname);voidset_money(longmoney);std::stringshow_money();private:// メンバ変数std::stringm_name;longm_money;};

classA.cppを見る
classA.cpp
//#include "pch.h"#include "classA.h"
#include <iostream>
#include <string>
#include <sstream>
ClassA::ClassA(){m_name="None";m_money=0;}ClassA::~ClassA(){}// 名前を登録するvoidClassA::set_name(std::stringname){m_name=name;}// 金額を代入するvoidClassA::set_money(longmoney){m_money=money;}// 金額を表示するstd::stringClassA::show_money(){std::stringstreamss;ss<<m_name<<" has "<<m_money<<" yen "<<std::endl;returnss.str();}

  • 新しくCppファイルを追加したので、binding.gypを変更します。
binding.gyp
{"targets":[{# ↓addon.cc内の NODE_API_MODULE(addon, InitAll) と同名にする
"target_name":"addon","cflags!":["-fno-exceptions"],"cflags_cc!":["-fno-exceptions"],# ↓必要な.ccファイルを全て記述する
# ワイルドカードを使って、native_cpp内のファイルを全て読み込む
"sources":["addon.cc","wrapper.cc","<!@(node -p \"require('fs').readdirSync('./native_cpp').map(f=>'native_cpp/'+f).join(' ')\")"],"include_dirs":["<!@(node -p \"require('node-addon-api').include\")"],"defines":['NAPI_DISABLE_CPP_EXCEPTIONS'],}]}
  • この時点で一旦確認を行いましょう。
terminal
$ npm install.>> gyp info ok と表示されればビルド完了

2. ネイティブC++クラスのラッピング

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

書き換えたコードは以下をご確認ください。

wrapper.hを見る
wrapper.h
#ifndef WRAPPER
#define WRAPPER
#include <napi.h> // 必要なヘッダ
#include "./native_cpp/classA.h"
classWrapper:publicNapi::ObjectWrap<Wrapper>{public:staticNapi::ObjectInit(Napi::Envenv,Napi::Objectexports);staticNapi::ObjectNewInstance(Napi::Envenv,constNapi::CallbackInfo&info);Wrapper(constNapi::CallbackInfo&info);~Wrapper();// クラスAのラッピング関数Napi::ValuesetName(constNapi::CallbackInfo&info);Napi::ValuesetMoney(constNapi::CallbackInfo&info);Napi::ValueshowMoney(constNapi::CallbackInfo&info);private:ClassA*m_classA;};#endif

wrapper.cppを見る
wrapper.cpp
#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("setName",&Wrapper::setName),InstanceMethod("setMoney",&Wrapper::setMoney),InstanceMethod("showMoney",&Wrapper::showMoney),});Napi::FunctionReference*constructor=newNapi::FunctionReference();*constructor=Napi::Persistent(func);env.SetInstanceData(constructor);exports.Set("Wrapper",func);returnexports;}// ---------------------------------------------------------- //// ---------- これより下で ClassAのラッピングを定義する ----------- //// ---------------------------------------------------------- //// コンストラクタWrapper::Wrapper(constNapi::CallbackInfo&info):Napi::ObjectWrap<Wrapper>(info){m_classA=newClassA();};Wrapper::~Wrapper(){deletem_classA;m_classA=nullptr;};// メンバ関数Napi::ValueWrapper::setName(constNapi::CallbackInfo&info){Napi::Envenv=info.Env();std::stringname=info[0].As<Napi::String>().ToString();m_classA->set_name(name);returnenv.Null();}Napi::ValueWrapper::setMoney(constNapi::CallbackInfo&info){Napi::Envenv=info.Env();intmoney=info[0].As<Napi::Number>().Int32Value();m_classA->set_money(money);returnenv.Null();}Napi::ValueWrapper::showMoney(constNapi::CallbackInfo&info){Napi::Envenv=info.Env();std::stringans=m_classA->show_money();returnNapi::String::New(env,ans);}

3. javaScriptからC++クラスを使ってみる

  • では実際に、index.jsからCppクラスを呼び出してみましょう。
    • index.jsを以下のように書き直します。
index.js
// addon.cc内の NODE_API_MODULE(addon, InitAll) が呼ばれるvarWrapper=require('bindings')('addon');// addon.cc内の CreateObject() が呼ばれるvarobj=newWrapper()// wrapper.cc内でラッピングしたClassAの関数が使えるobj.setName("Tanaka Taro");obj.setMoney(1000);console.log(obj.showMoney());
  • ターミナルからコマンドを実行し、下記のように表示されれば成功です。
terminal
$ node .>> Tanaka Taro has 1000 yen

4. dllを利用する

  • dllを利用するための手順は簡単です。
    • .libファイルを準備する(Windowsの場合)
    • binding.gypにlibrariesセクションを追加し、そこに追加した.libファイルのパスを記入する
    • ターミナルで npm install .を実行し、ビルドする
    • ./build/Releaseフォルダに .dllファイルを追加する(Windowsの場合)

Example

  • 第二章で作成したプロジェクトを以下のように変更します。
    カレントディレクトリ
     ├── node_modules
     ├── package-lock.json
     ├── package.json
     ├── index.js
     ├── addon.cc
     ├── binding.gyp
     ├── wrapper.cc
     ├── wrapper.h
     ├── native_cpp
         ├── classA.cpp<-- 削除
         ├── classA.lib <-- 追加
         └── classA.h
     ├── build
         ├── Release
           ├── classA.dll <-- 追加 (Windowsの場合)

  • 次に、binding.gypを編集し、include_directoriesセクションを追加します。

binding.gyp
{"targets":[{"target_name":"addon","cflags!":["-fno-exceptions"],"cflags_cc!":["-fno-exceptions"],"sources":["addon.cc","wrapper.cc","<!@(node -p \"require('fs').readdirSync('./native_cpp').map(f=>'native_cpp/'+f).join(' ')\")"],"include_dirs":["<!@(node -p \"require('node-addon-api').include\")"],# 【追加】ここにライブラリファイルを登録する
"libraries":["<(module_root_dir)/native_cpp/libclassA.dylib"],"defines":['NAPI_DISABLE_CPP_EXCEPTIONS'],}]}
  • 最後に、ビルド&実行して終了です。
terminal
$ npm install.$ node .

Viewing all articles
Browse latest Browse all 8691

Trending Articles