Maison  >  Article  >  interface Web  >  Trois façons d'implémenter des composants dans Node.js_node.js

Trois façons d'implémenter des composants dans Node.js_node.js

WBOY
WBOYoriginal
2016-05-16 15:13:441618parcourir

Présentez d'abord la différence entre l'utilisation de l'API v8 et l'utilisation du framework swig :

(1) La méthode API v8 est la méthode native fournie par le fonctionnaire, avec des fonctions puissantes et complètes. L'inconvénient est qu'il faut se familiariser avec l'API v8, qui est plus compliquée à écrire. vers js et ne peut pas facilement prendre en charge d'autres langages de script.

(2) swig est un support tiers, un puissant outil de développement de composants qui prend en charge la génération de code d'empaquetage de composants C++ pour une variété de langages de script courants tels que python, lua, js, etc. écrire du code C++ et des fichiers de configuration swig Vous pouvez développer des composants C++ dans divers langages de script sans connaître les cadres de développement de composants de divers langages de script. L'inconvénient est qu'il ne prend pas en charge les rappels JavaScript, la documentation et le code de démonstration sont incomplets, et là. il n'y a pas beaucoup d'utilisateurs.

1. Pure JS pour implémenter les composants Node.js
(1) Accédez au répertoire helloworld et exécutez npm init pour initialiser package.json. Ignorez les différentes options et laissez-les par défaut.

(2) Implémentation du composant index.js, par exemple :

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

(3) Exécuter dans le répertoire externe : npm install ./helloworld, helloworld est ensuite installé dans le répertoire node_modules.
(4) Écrivez le code d'utilisation du composant :

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

2. Utilisez l'API v8 pour implémenter les composants JS - mode synchrone
(1) Écrivez bind.gyp, par exemple :

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

(2) Écrivez l'implémentation du composant hello.cpp, par exemple :

#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) Compiler le composant

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

(4) Écrire le code js de test

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

(5) Ajoutez package.json pour l'installation, par exemple :

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

(5) Installer les composants sur node_modules

Allez dans le répertoire supérieur du répertoire du composant et exécutez : npm install ./helloc //Remarque : helloc est le répertoire du composant
Le module hello sera installé dans le répertoire node_modules du répertoire courant. Le code du test s'écrit comme ceci :
.

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

3. Utilisez l'API v8 pour implémenter les composants JS - mode asynchrone
La description ci-dessus est un composant synchrone. foo() est une fonction synchrone, c'est-à-dire que l'appelant de la fonction foo() doit attendre la fin de l'exécution de la fonction foo() avant de continuer. Opération fastidieuse d'IO, fonction, la fonction asynchrone foo() peut réduire les attentes de blocage et améliorer les performances globales.

Pour implémenter des composants asynchrones, il vous suffit de prêter attention à l'API uv_queue_work de libuv. Lors de l'implémentation du composant, à l'exception du code principal hello.cpp et du code utilisateur du composant, les autres parties sont cohérentes avec les trois démos ci-dessus.

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

L'idée de l'asynchrone est très simple. Pour implémenter une fonction de travail, une fonction d'achèvement et une structure qui transporte des données pour la transmission entre threads, appelez simplement uv_queue_work. La difficulté réside dans la familiarité avec la structure de données et l'API v8.

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 implémente les composants Node.js
Utilisez le framework swig pour écrire des composants Node.js

(1) Écrire l'implémentation du composant : *.h et *.cpp

par exemple :

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

(2) Écrivez *.i, utilisé pour générer le fichier cpp d'emballage swig
par exemple :

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

Le %apply ci-dessus signifie que le résultat int*, int* xx, std::string* result, std::string* yy, std::string& result dans le code sont des descriptions de sortie. Il s'agit d'un typemap, qui est. un remplacement.
Si les paramètres de pointeur dans les paramètres de la fonction C++ renvoient une valeur (spécifiée via OUTPUT dans le fichier *.i), Swig les traitera comme valeur de retour de la fonction JS. S'il y a plusieurs pointeurs, la valeur de retour de la fonction JS. est une liste.
%template(vectori) vectorbd43222e33876353aff11e13a7dc75f6 signifie qu'un type vectori est défini pour JS. Il s'agit généralement d'une fonction C++ qui utilise vectorbd43222e33876353aff11e13a7dc75f6 comme paramètre ou valeur de retour.
(3) Écrivez bind.gyp pour la compilation en utilisant node-gyp
(4) Générez le fichier warpper cpp. Faites attention aux informations de version v8 lors de la génération, par exemple : swig -javascript -node -c++ -DV8_VERSION=0x040599 example.i
. (5) Compiler et tester
La difficulté réside dans l’utilisation des types stl et des types personnalisés. Il existe trop peu de documents officiels à cet égard.
swig - Utilisation de l'encapsulation JavaScript de std::vector, std::string, voir : Mon exercice, principalement axé sur l'implémentation de fichiers *.i.
5. Autres
Lors de l'utilisation de l'API v8 pour implémenter des composants Node.js, vous pouvez trouver des similitudes avec l'implémentation des composants Lua. Lua a une machine à états et Node a Isolate.

Lorsque Node implémente l'exportation d'objets, il doit implémenter un constructeur, y ajouter une "fonction membre" et enfin exporter le constructeur en tant que nom de classe. Lorsque Lua implémente l'exportation d'objets, il doit également implémenter une fonction d'usine pour créer des objets, et doit également ajouter des « fonctions membres » à la table. Enfin, exportez la fonction d'usine.

Le script js de Node a le nouveau mot-clé, mais pas Lua, donc Lua ne fournit que des usines d'objets externes pour créer des objets, tandis que Node peut fournir des usines d'objets ou une encapsulation de classe.

Ce qui précède représente l’intégralité du contenu de cet article, j’espère qu’il sera utile à l’étude de chacun.

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn