Cómo crear plugins C / C ++ en el node

    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

    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.10y 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:

    "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 realmente nan.h debería usarse en su lugar
    • 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!

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *