Как создавать надстройки C / C ++ в Node

Node.js хорош по многим причинам, одна из которых - скорость, с которой вы можете создавать значимые приложения. Однако, как мы все знаем, это происходит за счет производительности (по сравнению с собственным кодом). Чтобы обойти это, вы можете написать свой код для взаимодействия с более быстрым кодом, написанным на C или C ++. Все, что нам нужно сделать, это сообщить Node, где найти этот код и как с ним взаимодействовать. Есть несколько способов решить эту проблему в зависимости от того, какой уровень абстракции вы хотите. Мы начнем с

Node.js хорош по многим причинам, одна из которых - скорость, с которой вы можете создавать значимые приложения. Однако, как мы все знаем, это происходит за счет производительности (по сравнению с собственным кодом). Чтобы обойти это, вы можете написать свой код для взаимодействия с более быстрым кодом, написанным на C или C ++. Все, что нам нужно сделать, это сообщить Node, где найти этот код и как с ним взаимодействовать.

Есть несколько способов решить эту проблему в зависимости от того, какой уровень абстракции вы хотите. Мы начнем с самой низкой абстракции, а именно с Node Addon .

Дополнения

Аддон работает, обеспечивая связь между библиотеками Node и C / C ++. Для типичного разработчика Node это может быть немного сложно, так как вам придется фактически написать код C / C ++ для настройки интерфейса. Однако между этой статьей и документацией по Node вы сможете заставить работать несколько простых интерфейсов.

Есть несколько вещей, которые нам нужно сделать, прежде чем мы сможем приступить к созданию аддонов. Прежде всего, нам нужно знать, как компилировать (о чем разработчики Node к счастью забывают) собственный код. Это делается с помощью node-gyp . Затем мы кратко поговорим о nan , который помогает обрабатывать различные версии Node API.

узел-гипс

Существует множество различных типов процессоров (x86, ARM, PowerPC и т. Д.) И даже больше операционных систем, с которыми приходится иметь дело при компиляции кода. К счастью, за вас все это node-gyp Как описано на их странице Github, node-gyp - это «кроссплатформенный инструмент командной строки, написанный на Node.js для компиляции собственных дополнительных модулей для Node.js». По сути, node-gyp - это просто оболочка вокруг gyp , созданная командой Chromium.

В README проекта есть отличные инструкции по установке и использованию пакета, поэтому вам следует прочитать их для получения более подробной информации. Короче говоря, чтобы использовать node-gyp вам нужно сделать следующее.

Перейдите в каталог вашего проекта:

 $ cd my_node_addon 

Сгенерируйте соответствующие файлы сборки с помощью команды configure , которая создаст либо Makefile (в Unix), либо vcxproj (в Windows):

 $ node-gyp configure 

И наконец, соберите проект:

 $ node-gyp build 

Это создаст /build содержащий, среди прочего, скомпилированный двоичный файл.

Даже при использовании более высоких абстракций, таких как ffi , все же хорошо понимать, что происходит под капотом, поэтому я рекомендую вам потратить время на изучение тонкостей node-gyp .

нан

nan (собственные абстракции для Node) - это модуль, о котором легко забыть, но он избавит вас от многих часов разочарования. Между версиями Node v0.8 , v0.10 и v0.12 используемые версии V8 претерпели некоторые большие изменения (в дополнение к изменениям внутри самого узла), поэтому nan помогает скрыть эти изменения от вас и обеспечивает приятный согласованный интерфейс. .

Эта собственная абстракция работает, предоставляя объекты / функции C / C ++ в файле заголовка #include <nan.h>

Чтобы использовать его, установите пакет nan

 $ npm install --save nan 

Добавьте эти строки в свой файл binding.gyp:

 "include_dirs" : [ 
 "<!(node -e \"require('nan')\")" 
 ] 

И вы готовы использовать методы / функции из nan.h в своих хуках вместо исходного кода #include <node.h> Я очень рекомендую вам использовать nan . В этом случае нет особого смысла изобретать колесо.

Создание аддона

Перед тем, как начать работу над аддоном, убедитесь, что у вас есть время, чтобы ознакомиться со следующими библиотеками:

  • Библиотека V8 JavaScript C ++, которая используется для фактического взаимодействия с JavaScript (например, для создания функций, вызова объектов и т. Д.).
    • Примечание: node.h файл по умолчанию предлагается, но на самом деле nan.h следует использовать вместо
  • libuv , кроссплатформенная библиотека асинхронного ввода-вывода, написанная на C. Эта библиотека полезна при выполнении любого типа ввода-вывода (открытие файла, запись в сеть, установка таймера и т. д.) в ваших собственных библиотеках и вы нужно сделать его асинхронным.
  • Внутренние библиотеки узлов. Одним из наиболее важных объектов для понимания является node::ObjectWrap , от которого происходит большинство объектов.

В оставшейся части этого раздела я покажу вам реальный пример. В этом случае мы создадим перехватчик функции pow библиотеки <cmath> Поскольку вы почти всегда должны использовать nan , я буду использовать его во всех примерах.

В этом примере в вашем проекте аддона должны присутствовать как минимум следующие файлы:

  • pow.cpp
  • binding.gyp
  • package.json

Файл C ++ не обязательно называть pow.cpp , но имя обычно отражает либо то, что это надстройка, либо его конкретная функция.

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

Обратите внимание, что в конце NODE_MODULE нет точки с запятой ( ; ). Это сделано намеренно, поскольку NODE_MODULE самом деле не функция

  • это макрос.

Приведенный выше код поначалу может показаться немного устрашающим для тех, кто не писал на C ++ какое-то время (или когда-либо), но на самом деле это не так уж сложно понять. Функция Pow - это основа кода, в котором мы проверяем количество переданных аргументов, типы аргументов, вызываем встроенную pow и возвращаем результат в приложение Node. info объект содержит все, что нам нужно знать о вызове, включая аргументы (и их типы) и место для возврата результата.

Функция Init основном просто связывает функцию Pow
имя "pow", а NODE_MODULE фактически обрабатывает регистрацию надстройки в Node.

package.json мало чем отличается от обычного модуля Node. Хотя это не кажется обязательным, в большинстве модулей Addon установлено значение "gypfile": true , но процесс сборки, кажется, все еще работает без него. Вот что я использовал для этого примера:

 { 
 "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" 
 } 
 } 

Затем этот код необходимо встроить в файл «pow.node», который является двоичным файлом аддона. Для этого вам нужно указать node-gyp какие файлы ему нужно скомпилировать, и имя файла, полученного в результате двоичного файла. Хотя есть много других опций / конфигураций, которые вы можете использовать с node-gyp , для этого примера нам не нужно много. binding.gyp может быть таким простым, как:

 { 
 "targets": [ 
 { 
 "target_name": "pow", 
 "sources": [ "pow.cpp" ], 
 "include_dirs": [ 
                "<!(node -e \"require('nan')\")" 
 ] 
 } 
 ] 
 } 

Теперь, используя node-gyp , сгенерируйте соответствующие файлы сборки проекта для данной платформы:

 $ node-gyp configure 

И наконец, соберите проект:

 $ node-gyp build 

Это должно привести к pow.node файла pow.node, который будет находиться в каталоге build/Release/ Для того, чтобы использовать этот крюк в коде приложения, просто require в pow.node файл (без «.node» расширение):

 var addon = require('./build/Release/pow'); 
 
 console.log(addon.pow(4, 2)); // Prints '16' 

Интерфейс внешней функции узла

Примечание . Пакет ffi ранее назывался node-ffi . Обязательно добавьте новое ffi в свои зависимости, чтобы избежать путаницы во время npm install :)

Хотя функциональность Addon, предоставляемая Node, дает вам всю необходимую гибкость, не всем разработчикам / проектам она понадобится. Во многих случаях такая абстракция, как ffi подойдет и обычно требует очень небольшого программирования на C / C ++ или вообще не требует его.

ffi загружает только динамические библиотеки, что может быть ограничивающим для некоторых, но также значительно упрощает настройку хуков.

 var ffi = require('ffi'); 
 
 var libm = ffi.Library('libm', { 
 'pow': [ 'double', [ 'double', 'double' ] ] 
 }); 
 
 console.log(libm.pow(4, 2)); // 16 

Приведенный выше код работает, указывая библиотеку для загрузки (libm) и, в частности, какие методы загружать из этой библиотеки (pow). [ 'double', [ 'double', 'double' ] ] строка говорит ffi , что тип возвращаемого значения и параметры метода в том, что в этом случае состоит из двух double параметров и double вернулся.

Заключение

Хотя сначала это может показаться пугающим, создание аддона на самом деле не так уж и плохо после того, как у вас будет возможность самостоятельно проработать такой небольшой пример. По возможности я бы предложил подключиться к динамической библиотеке, чтобы значительно упростить создание интерфейса и загрузку кода, хотя для многих проектов это может быть невозможно или лучший выбор.

Есть ли примеры библиотек, для которых вы хотели бы видеть привязки? Дайте нам знать об этом в комментариях!

comments powered by Disqus