Heim  >  Artikel  >  Web-Frontend  >  Drei Möglichkeiten, Komponenten in Node.js_node.js zu implementieren

Drei Möglichkeiten, Komponenten in Node.js_node.js zu implementieren

WBOY
WBOYOriginal
2016-05-16 15:13:441620Durchsuche

Stellen Sie zunächst den Unterschied zwischen der Verwendung der v8-API und der Verwendung des Swig-Frameworks vor:

(1) Die v8-API-Methode ist die vom Beamten bereitgestellte native Methode mit leistungsstarken und vollständigen Funktionen. Der Nachteil besteht darin, dass Sie mit der v8-API vertraut sein müssen, die schwieriger zu schreiben ist zu js und kann andere Skriptsprachen nicht ohne weiteres unterstützen.

(2) Swig ist ein Drittanbieter-Support, ein leistungsstarkes Komponentenentwicklungstool, das die Generierung von C++-Komponentenverpackungscode für eine Vielzahl gängiger Skriptsprachen wie Python, Lua, JS usw. unterstützt. Swig-Benutzer müssen dies nur tun Schreiben Sie C++-Code und SWIG-Konfigurationsdateien. Sie können C++-Komponenten in verschiedenen Skriptsprachen entwickeln, ohne die Komponentenentwicklungsframeworks verschiedener Skriptsprachen zu kennen. Der Nachteil besteht darin, dass JavaScript-Rückrufe nicht unterstützt werden und die Dokumentation und der Democode unvollständig sind Es gibt nicht viele Benutzer.

1. Reines JS zur Implementierung von Node.js-Komponenten
(1) Gehen Sie in das Verzeichnis helloworld und führen Sie npm init aus, um package.json zu initialisieren. Ignorieren Sie die verschiedenen Optionen und belassen Sie sie als Standard.

(2) Komponentenimplementierung index.js, zum Beispiel:

module.exports.Hello = function(name) {
    console.log('Hello ' + name);
}

(3) Im äußeren Verzeichnis ausführen: npm install ./helloworld, helloworld wird dann im Verzeichnis node_modules installiert.
(4) Komponentenverwendungscode schreiben:

var m = require('helloworld');
m.Hello('zhangsan');
//输出: Hello zhangsan

2. Verwenden Sie die v8-API, um JS-Komponenten zu implementieren – synchroner Modus
(1) Schreiben Sie binding.gyp, z. B.:

{
 "targets": [
  {
   "target_name": "hello",
   "sources": [ "hello.cpp" ]
  }
 ]
}

(2) Schreiben Sie die Implementierung der Komponente hello.cpp, z. B.:

#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) Komponente kompilieren

node-gyp configure
node-gyp build
./build/Release/目录下会生成hello.node模块。

(4) Test-JS-Code schreiben

const m = require('./build/Release/hello')
console.log(m.foo()); //输出 Hello World

(5) Fügen Sie package.json für die Installation hinzu, z. B.:

{                                                                                                         
  "name": "hello",
  "version": "1.0.0",
  "description": "", 
  "main": "index.js",
  "scripts": {
    "test": "node test.js"
  }, 
  "author": "", 
  "license": "ISC"
}

(5) Komponenten in node_modules installieren

Gehen Sie in das übergeordnete Verzeichnis des Komponentenverzeichnisses und führen Sie Folgendes aus: npm install ./helloc //Hinweis: helloc ist das Komponentenverzeichnis
Das Hello-Modul wird im Verzeichnis node_modules im aktuellen Verzeichnis installiert. Der Testcode lautet wie folgt:

var m = require('hello');
console.log(m.foo());  

3. Verwenden Sie die v8-API, um JS-Komponenten zu implementieren – asynchroner Modus
Bei der obigen Beschreibung handelt es sich um eine synchrone Funktion. Das heißt, der Aufrufer der Funktion foo() muss warten, bis die Ausführung der Funktion foo() abgeschlossen ist, bevor er fortfahren kann Der zeitaufwändige IO-Vorgang und die asynchrone Funktion foo() können Blockierungswartezeiten reduzieren und die Gesamtleistung verbessern.

Um asynchrone Komponenten zu implementieren, müssen Sie bei der Implementierung der Komponente nur auf die uv_queue_work-API achten, mit Ausnahme des Hauptcodes hello.cpp und des Komponentenbenutzercodes stimmen andere Teile mit den oben genannten drei Demos überein.

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

Die Idee von Asynchronität ist sehr einfach. Um eine Arbeitsfunktion, eine Vervollständigungsfunktion und eine Struktur zu implementieren, die Daten für die Thread-übergreifende Übertragung überträgt, rufen Sie einfach uv_queue_work auf. Die Schwierigkeit liegt in der Vertrautheit mit der Datenstruktur und API der Version 8.

test.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 implementiert Node.js-Komponenten
Verwenden Sie das Swig-Framework, um Node.js-Komponenten zu schreiben

(1) Schreiben Sie die Implementierung der Komponente: *.h und *.cpp

zB:

namespace a {
  class A{
  public:
    int add(int a, int y);
  };
  int add(int x, int y);
}

(2) Schreiben Sie *.i, um die CPP-Datei für die Swig-Verpackung zu generieren
zB:

/* 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>;
};

Das obige %apply bedeutet, dass int* result, int* xx, std::string* yy, std::string& result im Code Ausgabebeschreibungen sind ein Ersatz.
Wenn die Zeigerparameter in den C++-Funktionsparametern einen Wert zurückgeben (angegeben durch OUTPUT in der *.i-Datei), verarbeitet Swig sie als Rückgabewert der JS-Funktion. Wenn mehrere Zeiger vorhanden sind, wird der Rückgabewert der JS-Funktion verwendet ist Liste.
%template(vectori) vectorbd43222e33876353aff11e13a7dc75f6 bedeutet, dass ein Typ vectori für JS definiert ist. Dies ist normalerweise eine C++-Funktion, die vectorbd43222e33876353aff11e13a7dc75f6 verwendet.
(3) Schreiben Sie binding.gyp zur Kompilierung mit node-gyp
(4) Generieren Sie die Warpper-CPP-Datei. Achten Sie beim Generieren auf die V8-Versionsinformationen, z. B.: swig -javascript -node -c++ -DV8_VERSION=0x040599 example.i
(5) Kompilieren und testen
Die Schwierigkeit liegt in der Verwendung von STL-Typen und benutzerdefinierten Typen. Hierzu gibt es zu wenige offizielle Dokumente.
swig – Verwendung von std::vector, std::string in JavaScript-Kapselung, siehe: Meine Übung, die sich hauptsächlich auf die Implementierung von *.i-Dateien konzentriert.
5. Andere
Wenn Sie die v8-API zum Implementieren von Node.js-Komponenten verwenden, können Sie Ähnlichkeiten mit der Implementierung von Lua-Komponenten feststellen. Lua verfügt über eine Zustandsmaschine und Node über Isolate.

Wenn Node den Objektexport implementiert, muss er einen Konstruktor implementieren, ihm eine „Mitgliedsfunktion“ hinzufügen und schließlich den Konstruktor als Klassennamen exportieren. Wenn Lua den Objektexport implementiert, muss es auch eine Factory-Funktion zum Erstellen von Objekten implementieren und der Tabelle auch „Mitgliedsfunktionen“ hinzufügen. Exportieren Sie abschließend die Factory-Funktion.

Das js-Skript von Node hat das Schlüsselwort new, Lua jedoch nicht, sodass Lua nur externe Objektfabriken zum Erstellen von Objekten bereitstellt, während Node Objektfabriken oder Klassenkapselung bereitstellen kann.

Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, er wird für das Studium aller hilfreich sein.

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn