Node.js es excelente por muchas razones, una de las cuales es la velocidad con la que puede crear aplicaciones significativas. Sin embargo, como todos sabemos, esto tiene el precio del rendimiento (en comparación con el código nativo). Para evitar esto, puede escribir su código para interactuar con un código más rápido escrito en C o C ++. Todo lo que tenemos que hacer es informar a Node dónde encontrar este código y cómo interactuar con él.
Hay algunas formas de resolver este problema según el nivel de abstracción que desee. Comenzaremos con la abstracción más baja, que es el node Añadir.
Complementos
Contenido
Un plugin funciona proporcionando el pegamento entre las bibliotecas Node y C / C ++. Para el desarrollador típico de Node, esto puede ser un poco complicado, ya que tendrá que comenzar a escribir código C / C ++ para configurar la interfaz. Sin embargo, entre este artículo y la documentación de Node, debería poder hacer funcionar algunas interfaces simples.
Hay algunas cosas que debemos repasar antes de comenzar a crear plugins. En primer lugar, necesitamos saber cómo compilar (algo que los desarrolladores de Node felizmente olvidan) el código nativo. Esto se hace usando node-gyp. Luego, hablaremos brevemente sobre yaya, que ayuda a manejar diferentes versiones de API de node.
node-gyp
Hay muchos tipos diferentes de procesadores (x86, ARM, PowerPC, etc.) e incluso más sistemas operativos con los que lidiar al compilar su código. Por suerte, node-gyp
maneja todo esto por ti. Como se describe en su página de Github, node-gyp
es una «herramienta de línea de comandos multiplataforma escrita en Node.js para compilar módulos complementarios nativos para Node.js». Esencialmente, node-gyp
es solo un envoltorio estafa, que está hecho por el equipo de Chromium.
El archivo README del proyecto tiene excelentes instrucciones sobre cómo instalar y usar el paquete, por lo que debería leerlo para obtener más detalles. En resumen, usar node-gyp
debe hacer lo siguiente.
Vaya al directorio de su proyecto:
$ cd my_node_addon
Genere los archivos de compilación adecuados utilizando el configure
comando, que creará un Makefile
(en Unix), o vcxproj
(en Windows):
$ node-gyp configure
Y finalmente, construye el proyecto:
$ node-gyp build
Esto generará un /build
directorio que contiene, entre otras cosas, el binario compilado.
Incluso cuando se utilizan abstracciones superiores como ffi
paquete, es bueno entender lo que está sucediendo bajo el capó, así que le recomiendo que se tome el tiempo para aprender los entresijos node-gyp
.
yaya
nan
(Native Abstractions for Node) es un módulo que se pasa por alto fácilmente, pero le ahorrará horas de frustración. Entre versiones de node v0.8
, v0.10
y v0.12
, las versiones V8 utilizadas pasaron por algunos cambios importantes (además de cambios dentro del propio node), por lo que nan
ayuda a ocultarle estos cambios y proporciona una interfaz agradable y coherente.
Esta abstracción nativa funciona proporcionando objetos / funciones C / C ++ en el #include <nan.h>
archivo de cabecera.
Para usarlo, instale el nan
paquete:
$ npm install --save nan
Agregue estas líneas a su archivo binding.gyp:
Te puede interesar:¿Qué es Maven?"include_dirs" : [
"<!(node -e "require('nan')")"
]
Y estás listo para usar métodos / funciones desde nan.h
dentro de tus ganchos en lugar del original #include <node.h>
código. Te recomiendo encarecidamente que uses nan
. No tiene mucho sentido reinventar la rueda en este caso.
Crear el plugin
Antes de comenzar con su plugin, asegúrese de tomarse un tiempo para familiarizarse con las siguientes bibliotecas:
- La biblioteca V8 JavaScript C ++, que se utiliza para interactuar realmente con JavaScript (como crear funciones, llamar a objetos, etc.).
- NOTA:
node.h
es el archivo predeterminado sugerido, pero realmentenan.h
debería usarse en su lugar
- NOTA:
- libuv, una biblioteca de E / S asíncrona multiplataforma escrita en C. Esta biblioteca es útil cuando se realiza cualquier tipo de E / S (abrir un archivo, escribir en la red, configurar un temporizador, etc.) en sus bibliotecas nativas y necesita para hacerlo asincrónico.
- Bibliotecas de nodes internos. Uno de los objetos más importantes para comprender es
node::ObjectWrap
, del que derivan la mayoría de los objetos.
A lo largo del resto de esta sección, lo guiaré a través de un ejemplo real. En este caso, crearemos un enlace a C ++ <cmath>
de la biblioteca pow
función. Dado que casi siempre deberías usar nan
, eso es lo que usaré a lo largo de los ejemplos.
Para este ejemplo, en su proyecto de plugin debe tener al menos estos archivos presentes:
pow.cpp
binding.gyp
package.json
El archivo C ++ no necesita ser nombrado pow.cpp
, pero el nombre normalmente refleja que es un plugin o su función específica.
// pow.cpp
#include <cmath>
#include <nan.h>
void Pow(const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 2) {
Nan::ThrowTypeError("Wrong number of arguments");
return;
}
if (!info[0]->IsNumber() || !info[1]->IsNumber()) {
Nan::ThrowTypeError("Both arguments should be numbers");
return;
}
double arg0 = info[0]->NumberValue();
double arg1 = info[1]->NumberValue();
v8::Local<v8::Number> num = Nan::New(pow(arg0, arg1));
info.GetReturnValue().Set(num);
}
void Init(v8::Local<v8::Object> exports) {
exports->Set(Nan::New("pow").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(Pow)->GetFunction());
}
NODE_MODULE(pow, Init)
Tenga en cuenta que no hay punto y coma (;
) al final de NODE_MODULE
. Esto se hace intencionalmente ya que NODE_MODULE
en realidad no es una función, es una macro.
El código anterior puede parecer un poco abrumador al principio para aquellos que no han escrito C ++ en un tiempo (o nunca), pero en realidad no es demasiado difícil de entender. los Pow
La función es la carne del código donde verificamos el número de argumentos pasados, los tipos de argumentos, llamamos al nativo pow
función y devolver el resultado a la aplicación Node. los info
El objeto contiene todo lo que necesitamos saber sobre la llamada, incluidos los argumentos (y sus tipos) y un lugar para devolver el resultado.
los Init
la función principalmente solo asocia el Pow
funcionar con el
el nombre de «pow» y el NODE_MODULE
macro en realidad maneja el registro del plugin con Node.
los package.json
El archivo no es muy diferente de un módulo de node normal. Aunque no parece ser necesario, la mayoría de los módulos adicionales tienen "gypfile": true
establecido dentro de ellos, pero el proceso de compilación parece funcionar bien sin él. Esto es lo que usé para este ejemplo:
{
"name": "addon-hook",
"version": "0.0.0",
"description": "Node.js Addon Example",
"main": "index.js",
"dependencies": {
"nan": "^2.0.0"
},
"scripts": {
"test": "node index.js"
}
}
A continuación, este código debe integrarse en un archivo ‘pow.node’, que es el binario del plugin. Para hacer esto, necesitará decir node-gyp
qué archivos necesita compilar y el nombre de archivo resultante del binario. Si bien hay muchas otras opciones / configuraciones que puede usar con node-gyp
, para este ejemplo no necesitamos mucho. los binding.gyp
El archivo puede ser tan simple como:
{
"targets": [
{
"target_name": "pow",
"sources": [ "pow.cpp" ],
"include_dirs": [
"<!(node -e "require('nan')")"
]
}
]
}
Ahora, usando node-gyp
, genera los archivos de construcción del proyecto adecuados para la plataforma dada:
$ node-gyp configure
Y finalmente, construye el proyecto:
$ node-gyp build
Esto debería resultar en una pow.node
archivo que se crea, que residirá en el build/Release/
directorio. Para usar este gancho en el código de su aplicación, simplemente require
en el pow.node
archivo (sin la extensión ‘.node’):
var addon = require('./build/Release/pow');
console.log(addon.pow(4, 2)); // Prints '16'
Interfaz de función ajena de node
Nota: Los ffi
el paquete se conocía anteriormente como node-ffi
. Asegúrese de agregar el más nuevo ffi
nombre a sus dependencias para evitar mucha confusión durante npm install
🙂
Si bien la funcionalidad Addon proporcionada por Node le brinda toda la flexibilidad que necesita, no todos los desarrolladores / proyectos la necesitarán. En muchos casos, una abstracción como ffi
funcionará bien, y normalmente requiere muy poca o ninguna programación C / C ++.
ffi
solo carga bibliotecas dinámicas, lo que puede ser limitante para algunos, pero también facilita la configuración de los ganchos.
var ffi = require('ffi');
var libm = ffi.Library('libm', {
'pow': [ 'double', [ 'double', 'double' ] ]
});
console.log(libm.pow(4, 2)); // 16
El código anterior funciona especificando la biblioteca a cargar (libm), y específicamente qué métodos cargar desde esa biblioteca (pow). los [ 'double', [ 'double', 'double' ] ]
la línea dice ffi
cuál es el tipo de retorno y los parámetros del método, que en este caso son dos double
parámetros y un double
regresó.
Conclusión
Si bien puede parecer intimidante al principio, crear un plugin realmente no es tan malo después de haber tenido la oportunidad de trabajar con un pequeño ejemplo como este por su cuenta. Cuando sea posible, sugeriría conectarse a una biblioteca dinámica para facilitar la creación de la interfaz y la carga del código, aunque para muchos proyectos esto puede no ser posible o la mejor opción.
¿Hay ejemplos de bibliotecas para las que le gustaría ver enlaces? ¡Háznoslo saber en los comentarios!