Node.js_node.js でコンポーネントを実装する 3 つの方法

WBOY
リリース: 2016-05-16 15:13:44
オリジナル
1662 人が閲覧しました

まず、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());  
ログイン後にコピー
3. v8 API を使用して JS コンポーネントを実装する - 非同期モード

上記の説明は同期コンポーネントです。 foo() 関数は同期関数です。つまり、foo() 関数の呼び出し元は、foo() 関数が実行を完了するまで待つ必要があります。 IO に時間のかかる操作、関数、非同期 foo() 関数を使用すると、ブロッキング待機が軽減され、全体的なパフォーマンスが向上します。

非同期コンポーネントを実装するには、libuv の uv_queue_work API に注意するだけで済みます。メイン コード hello.cpp とコンポーネント ユーザー コードを除き、他の部分は上記の 3 つのデモと一致します。

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)
}

ログイン後にコピー
非同期の考え方は非常に簡単で、作業関数、完了関数、およびクロススレッド送信用のデータを運ぶ構造体を実装するには、uv_queue_work を呼び出すだけです。難しいのは、v8 のデータ構造と API に慣れることです。

テスト.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
*/

ログイン後にコピー
4. swig-javascript は Node.js コンポーネントを実装します

swig フレームワークを使用して Node.js コンポーネントを作成します

(1) コンポーネントの実装を作成します: *.h および *.cpp

例:

namespace a {
  class A{
  public:
    int add(int a, int y);
  };
  int add(int x, int y);
}
ログイン後にコピー
(2) swig パッケージングの cpp ファイルの生成に使用される *.i を書き込みます

例:

/* 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>;
};
ログイン後にコピー
上記の %apply は、コード内の int* result、int* xx、std::string* result、std::string* yy、std::string& result が出力の説明であることを意味します。交換品。

C++ 関数パラメータのポインタ パラメータが値 (*.i ファイルの OUTPUT で指定) を返す場合、Swig はそれらを JS 関数の戻り値として処理します。複数のポインタがある場合は、JS 関数の戻り値です。はリストです。
%template(vectori) Vector は、vectori 型が JS に対して定義されていることを意味します。これは、通常、vector をパラメータまたは戻り値として使用する C++ 関数です。
(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 がオブジェクトのエクスポートを実装する場合、コンストラクターを実装し、それに「メンバー関数」を追加し、最後にコンストラクターをクラス名としてエクスポートする必要があります。 Lua がオブジェクトのエクスポートを実装する場合、オブジェクトを作成するためのファクトリ関数も実装する必要があり、テーブルに「メンバー関数」を追加する必要もあります。最後に、ファクトリー関数をエクスポートします。

Node の js スクリプトには new キーワードがありますが、Lua にはありません。そのため、Lua はオブジェクトを作成するための外部オブジェクト ファクトリのみを提供しますが、Node はオブジェクト ファクトリまたはクラスのカプセル化を提供できます。

以上がこの記事の全内容です。皆様の学習のお役に立てれば幸いです。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート