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

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

$
0
0

はじめに

  • node.jsからC++関数を利用する際に、引数・返り値の渡し方について困ったことはありませんか?
    • この記事では、node-addon-apiを使う場合の、引数・返り値の受け渡し方をまとめています。

環境構築がまだの方は?

目次

1. 関数の基本形

  • node-addon-apiを利用する場合、ラップされるC++の関数は以下のように定義します。
    • Napi::Value 関数名(const Napi::CallbackInfo& info);
      • 引数の個数、型に関係なく、引数は const Napi::CallbackInfo& infoと記述
      • 返り値は、型に関係なくNapi::Valueと記述
  • 以下は、関数宣言と関数定義の例です。
    • 引数、返り値ともにvoidです。
void.h
#include <napi.h>
Napi::Valuefunc(constNapi::CallbackInfo&info);
void.cpp
#include <napi.h>
Napi::Valuefunc(constNapi::CallbackInfo&info){// Do nothing.returnenv.Null();}

2. 値の受け渡し

  • js ↔︎ C++でやり取りするにあたって必要な引数、返り値の処理についてまとめました。

引数の型対応表

C++型napi型
int.As<Napi::Number>().Int32Value()
double.As<Napi::Number>().DoubleValue()
std::string.As<Napi::String>().ToString()

返り値の型対応表

C++型napi型
int, doublereturn Napi::Number::New(env, C++変数名)
std::stringreturn Napi::String::New(env, C++変数名)
  • jsは数値型が1種類しかないため、数値であればNapi::Number型で良い

関数例

  • 例として、jsから2つの引数(a,b)をC++で受け取り、その和をjsに返却する関数add()を考えてみましょう。
example.cpp
#include <napi.h>
Napi::Valueadd(constNapi::CallbackInfo&info){// お約束Napi::Envenv=info.Env();// 引数は、配列infoから取り出す。doublea=info[0].As<Napi::Number>().DoubleValue();doubleb=info[1].As<Napi::Number>().DoubleValue();// C++で行いたい処理を行うdoubleans=a+b;// 返り値は、Napi::○○ 型にキャストして返却するreturnNapi::Number::New(env,ans);}
  • jsファイルからは次のように見えます
example.js
// (前処理は省略)letans=add(1,2)console.log(ans);// >> expected: 3

3. 配列の受け渡し

 引数に配列を渡す( js → C++ )

array.cpp
Napi::ValuesetArr(constNapi::CallbackInfo&info){Napi::Envenv=info.Env();Napi::Arrayarr=info[0].As<Array>();// C++の配列std::vector<double>vec(arr.Length(),0.0);// for文で要素を順に代入for(size_ti=0;i<arr.Length();i++){Napi::Valueval=arr[i];vec[i]=val.As<Napi::Number>().DoubleValue();}returnenv.Null();}

 返り値に配列を渡す( C++ → js )

array.cpp
Napi::ValuegetArr(constCallbackInfo&info){Napi::Envenv=info.Env();// C++の配列std::vector<double>vec={1.0,0.5,0.25};// for文で要素を順に代入Napi::ArrayoutArr=Napi::Array::New(env,vec.size());for(size_ti=0;i<vec.size();i++){outArr[i]=Napi::Number::New(env,vec[i]);}returnoutArr;}
  • jsファイルからは次のように利用します。
array.js
// (前処理は省略)setArr([1,2,3,4,5]);vararr=getArr();console.log(arr);// >> expected: [1.0, 0.5, 0.25]

4. (応用)異なるプリミティブ型を配列に入れて返却する

  • C++で複数の返り値を返したい場合に有効です。
    • リターンコードと計算結果の組み合わせなどを返却できます。
array2.cpp
Napi::ValuegetReturns(constCallbackInfo&info){Napi::Envenv=info.Env();// do Something C++// 返り値として、 1.0 と "aabbcc" を返却する例constintreturnArgNums=2;intzero=0;intone=1;Napi::ArrayretArr=Napi::Array::New(env,returnArgNums);retArr[zero]=Napi::Number::New(env,1.0);retArr[one]=Napi::String::New(env,"aabbcc");returnretArr;}
array2.js
// (前処理は省略)vararr=getReturns();console.log("ret1 =",arr[0],"ret2 =",arr[1]);// >> expected: ret1 = 1 ret2 = aabbcc

5. 試してみよう

  • 前回の記事で作成したプロジェクトのwrapper.h, wrapper.cpp, index.jsを下記のコードで上書きすると、本記事の内容を試すことができます!

 ├── node_modules
 ├── package-lock.json
 ├── package.json
 ├── index.js <-- 上書き
 ├── addon.cc
 ├── wrapper.cc <-- 上書き
 ├── wrapper.h <-- 上書き
 └── binding.gyp

サンプルコードはこちら

wrapper.hを開く
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);Napi::Valueadd(constNapi::CallbackInfo&info);// <-- 追加Napi::ValuesetArr(constNapi::CallbackInfo&info);// <-- 追加Napi::ValuegetArr(constNapi::CallbackInfo&info);// <-- 追加Napi::ValuegetReturns(constNapi::CallbackInfo&info);// <-- 追加private:doublem_value;};#endif

wrapper.ccを開く
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("add",&Wrapper::add),// <-- 追加InstanceMethod("setArr",&Wrapper::setArr),// <-- 追加InstanceMethod("getArr",&Wrapper::getArr),// <-- 追加InstanceMethod("getReturns",&Wrapper::getReturns),// <-- 追加});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);}// 引数・返り値の受け渡しNapi::ValueWrapper::add(constNapi::CallbackInfo&info){// お約束Napi::Envenv=info.Env();// 引数は、配列infoから取り出す。doublea=info[0].As<Napi::Number>().DoubleValue();doubleb=info[1].As<Napi::Number>().DoubleValue();// C++で行いたい処理を行うdoubleans=a+b;// 返り値は、Napi::○○ 型にキャストして返却するreturnNapi::Number::New(env,ans);}// 配列の受け取りNapi::ValueWrapper::setArr(constNapi::CallbackInfo&info){Napi::Envenv=info.Env();Napi::Arrayarr=info[0].As<Array>();// C++の配列std::vector<double>vec(arr.Length(),0.0);// for文で要素を順に代入for(size_ti=0;i<arr.Length();i++){Napi::Valueval=arr[i];vec[i]=val.As<Napi::Number>().DoubleValue();}returnenv.Null();}// 配列の返却Napi::ValueWrapper::getArr(constCallbackInfo&info){Napi::Envenv=info.Env();// C++の配列std::vector<double>vec={1.0,0.5,0.25};// for文で要素を順に代入Napi::ArrayoutArr=Napi::Array::New(env,vec.size());for(size_ti=0;i<vec.size();i++){outArr[i]=Napi::Number::New(env,vec[i]);}returnoutArr;}// プリミティブ型が混在した配列の返却Napi::ValueWrapper::getReturns(constCallbackInfo&info){Napi::Envenv=info.Env();// do Something C++// 返り値として、 1.0 と "aabbcc" を返却する例constintreturnArgNums=2;intzero=0;intone=1;Napi::ArrayretArr=Napi::Array::New(env,returnArgNums);retArr[zero]=Napi::Number::New(env,1.0);retArr[one]=Napi::String::New(env,"aabbcc");returnretArr;}

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());// ---- 本記事で追加した関数 ---- //letans=obj.add(1,2)console.log(ans);// >> expected: 3obj.setArr([1,2,3,4,5]);vararr=obj.getArr();console.log(arr);// >> expected: [1.0, 0.5, 0.25]vararr=obj.getReturns();console.log("ret1 =",arr[0],"ret2 =",arr[1]);// >> expected: ret1 = 1 ret2 = aabbcc// ---- 本記事で追加した関数 ---- //

その他

  • 返り値にユーザー定義型やオブジェクトを返す場合は このあたりが参考になるかもしれません。

Viewing all articles
Browse latest Browse all 8833

Trending Articles