まず、v8 API の使用と swig フレームワークの使用の違いを紹介します。
(1) v8 API メソッドは、強力で完全な機能を備えた公式が提供するネイティブ メソッドです。欠点は、v8 API に精通する必要があり、作成がより面倒であることです。 js に変換されるため、他のスクリプト言語を簡単にサポートすることはできません。
(2) swig はサードパーティのサポートであり、Python、lua、js などのさまざまな一般的なスクリプト言語用の C++ コンポーネント パッケージング コードの生成をサポートする強力なコンポーネント開発ツールです。swig ユーザーは次のことのみを行う必要があります。 C++ コードを記述し、設定ファイルを変更する さまざまなスクリプト言語のコンポーネント開発フレームワークを知らなくても、さまざまなスクリプト言語で C++ コンポーネントを開発できます。欠点は、JavaScript コールバックがサポートされていないこと、ドキュメントとデモ コードが不完全であることです。ユーザーは多くありません。
1. Node.js コンポーネントを実装するための Pure JS
(1) helloworld ディレクトリに移動し、npm init を実行して package.json を初期化します。さまざまなオプションを無視し、デフォルトのままにします。
(2) コンポーネント実装index.js、例:
module.exports.Hello = function(name) { console.log('Hello ' + name); }
(3) 外側のディレクトリで実行します: npm install ./helloworld、helloworld は、node_modules ディレクトリにインストールされます。
(4) コンポーネント使用コードを記述します:
var m = require('helloworld'); m.Hello('zhangsan'); //输出: Hello zhangsan
2. v8 API を使用して JS コンポーネントを実装する - 同期モード
(1) binding.gyp を書き込みます。例:
{ "targets": [ { "target_name": "hello", "sources": [ "hello.cpp" ] } ] }
(2) コンポーネント hello.cpp の実装を記述します。例:
#include <node.h> namespace cpphello { using v8::FunctionCallbackInfo; using v8::Isolate; using v8::Local; using v8::Object; using v8::String; using v8::Value; void Foo(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Hello World")); } void Init(Local<Object> exports) { NODE_SET_METHOD(exports, "foo", Foo); } NODE_MODULE(cpphello, Init) }
(3) コンポーネントをコンパイルします
node-gyp configure node-gyp build ./build/Release/目录下会生成hello.node模块。
(4) テスト用のjsコードを書く
const m = require('./build/Release/hello') console.log(m.foo()); //输出 Hello World
(5) インストール用に package.json を追加します。例:
{ "name": "hello", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "node test.js" }, "author": "", "license": "ISC" }
(5) コンポーネントをnode_modulesにインストールします
コンポーネント ディレクトリの上位ディレクトリに移動し、次のコマンドを実行します: npm install ./hloc //注: helloc はコンポーネント ディレクトリです
hello モジュールは現在のディレクトリの node_modules ディレクトリにインストールされます。テスト コードは次のように記述されます。
var m = require('hello'); console.log(m.foo());
上記の説明は同期コンポーネントです。 foo() 関数は同期関数です。つまり、foo() 関数の呼び出し元は、foo() 関数が実行を完了するまで待つ必要があります。 IO に時間のかかる操作、関数、非同期 foo() 関数を使用すると、ブロッキング待機が軽減され、全体的なパフォーマンスが向上します。
hello.cpp:
/* * Node.js cpp Addons demo: async call and call back. * gcc 4.8.2 * author:cswuyg * Date:2016.02.22 * */ #include <iostream> #include <node.h> #include <uv.h> #include <sstream> #include <unistd.h> #include <pthread.h> namespace cpphello { using v8::FunctionCallbackInfo; using v8::Function; using v8::Isolate; using v8::Local; using v8::Object; using v8::Value; using v8::Exception; using v8::Persistent; using v8::HandleScope; using v8::Integer; using v8::String; // async task struct MyTask{ uv_work_t work; int a{0}; int b{0}; int output{0}; unsigned long long work_tid{0}; unsigned long long main_tid{0}; Persistent<Function> callback; }; // async function void query_async(uv_work_t* work) { MyTask* task = (MyTask*)work->data; task->output = task->a + task->b; task->work_tid = pthread_self(); usleep(1000 * 1000 * 1); // 1 second } // async complete callback void query_finish(uv_work_t* work, int status) { Isolate* isolate = Isolate::GetCurrent(); HandleScope handle_scope(isolate); MyTask* task = (MyTask*)work->data; const unsigned int argc = 3; std::stringstream stream; stream << task->main_tid; std::string main_tid_s{stream.str()}; stream.str(""); stream << task->work_tid; std::string work_tid_s{stream.str()}; Local<Value> argv[argc] = { Integer::New(isolate, task->output), String::NewFromUtf8(isolate, main_tid_s.c_str()), String::NewFromUtf8(isolate, work_tid_s.c_str()) }; Local<Function>::New(isolate, task->callback)->Call(isolate->GetCurrentContext()->Global(), argc, argv); task->callback.Reset(); delete task; } // async main void async_foo(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); HandleScope handle_scope(isolate); if (args.Length() != 3) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "arguments num : 3"))); return; } if (!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsFunction()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "arguments error"))); return; } MyTask* my_task = new MyTask; my_task->a = args[0]->ToInteger()->Value(); my_task->b = args[1]->ToInteger()->Value(); my_task->callback.Reset(isolate, Local<Function>::Cast(args[2])); my_task->work.data = my_task; my_task->main_tid = pthread_self(); uv_loop_t *loop = uv_default_loop(); uv_queue_work(loop, &my_task->work, query_async, query_finish); } void Init(Local<Object> exports) { NODE_SET_METHOD(exports, "foo", async_foo); } NODE_MODULE(cpphello, Init) }
テスト.js
// test helloUV module 'use strict'; const m = require('helloUV') m.foo(1, 2, (a, b, c)=>{ console.log('finish job:' + a); console.log('main thread:' + b); console.log('work thread:' + c); }); /* output: finish job:3 main thread:139660941432640 work thread:139660876334848 */
swig フレームワークを使用して Node.js コンポーネントを作成します
例:
namespace a { class A{ public: int add(int a, int y); }; int add(int x, int y); }
例:
/* File : IExport.i */ %module my_mod %include "typemaps.i" %include "std_string.i" %include "std_vector.i" %{ #include "export.h" %} %apply int *OUTPUT { int *result, int* xx}; %apply std::string *OUTPUT { std::string* result, std::string* yy }; %apply std::string &OUTPUT { std::string& result }; %include "export.h" namespace std { %template(vectori) vector<int>; %template(vectorstr) vector<std::string>; };
C++ 関数パラメータのポインタ パラメータが値 (*.i ファイルの OUTPUT で指定) を返す場合、Swig はそれらを JS 関数の戻り値として処理します。複数のポインタがある場合は、JS 関数の戻り値です。はリストです。
%template(vectori) Vector
(3)node-gyp
を使用してコンパイル用の binding.gyp を作成します
(4) warpper cpp ファイルを生成します。生成するときは、v8 バージョン情報に注意してください。例: swig -javascript -node -c++ -DV8_VERSION=0x040599 example.i
(5) コンパイル&テスト
問題は、stl 型とカスタム型の使用にあります。この点に関する公式ドキュメントは少なすぎます。
swig - std::vector、std::string の JavaScript カプセル化の使用。主に *.i ファイルの実装に焦点を当てた私の演習を参照してください。
5.その他
v8 API を使用して Node.js コンポーネントを実装する場合、Lua コンポーネントの実装との類似点がわかります。Lua にはステート マシンがあり、Node には Isolate があります。
Node の js スクリプトには new キーワードがありますが、Lua にはありません。そのため、Lua はオブジェクトを作成するための外部オブジェクト ファクトリのみを提供しますが、Node はオブジェクト ファクトリまたはクラスのカプセル化を提供できます。
以上がこの記事の全内容です。皆様の学習のお役に立てれば幸いです。