Рубрики
Без рубрики

Грунтовка в родные модули Nodejs

Примечание: эта статья не охватывает ничего относительно WASM Standard. Здесь обсуждается только старомодный API C ++ для создания модулей Node.js. Много…

Автор оригинала: Andrei Glingeanu.

Примечание: эта статья не охватывает ничего относительно WASM Standard. Вот Обсудили только старомодный API C ++ для создания модулей Node.js.

А Лот Было сказано на интернатах о предмете написания модулей в C ++ для Node.js ( здесь и здесь ). А много Абстракции были построены ( здесь и здесь ). Большинство из них не сможет победить надежность и краткость Официальные документы Но я собираюсь взять это немного медленнее и просто документируйте, как я прибыл на очень базовый уровень Понимание предмета. Это исследование, конечно, просто очень простое Начальная точка с большим количеством деталей, оставленных, чтобы быть изменены, когда мы движемся вперед.

Жизненный цикл модуля узла

Прежде всего, нам нужно понять тот факт, что для того, чтобы иметь возможность использовать C ++ Код от JavaScript, нам нужно получить код C ++, компилируемый в специальный двоичный файл. Эти файлы заканчиваются .node расширение, и они содержат низкоуровневый Представление модуля Node.js. Узел требуют () Функция знает, как Обратите внимание на них должным образом и правильно скомпилированным модулем C ++ просто работает из коробка.

Вот как требуется инструкция по эксплуатации:

const nativeModule = require('./build/Release/native')

В этом случае модуль называется Native.node и очень часто они расположены В Build/Release папка относительно корневой папки проекта. Больше на этом Структура папки позже.

Запуск мира Hello

Мы начнем с обязательного мира Hello.

Вам понадобится инструментарий C ++, уже установленный в вашей системе ( G ++ на Unix-Alke Системы и Visual Studio на Windows). Подробнее можно прочитать на Узел-GYP файл readme).

mkdir native-modules
cd native-modules
touch binding.gyp
touch package.json
touch main.js
# Here we're going to put C++ source code
touch main.cpp

Заполнить Package.json :

{
  "name": "node-native-modules-hello-world",
  "version": "0.0.1",
  "main": "index.js",
  "license": "MIT",
  "gypfile": true,
  "scripts": {
    "install": "node-gyp rebuild",
    "start": "node index.js"
  }
}

Положить Узел-GYP Rebuild команда в Установить Сценарий удостоверится, что ваш родные модули будут скомпилированы каждый раз, когда вы запускаете NPM установить это на самом деле называется крючком и Вот Больше их. Не беспокойтесь о Узел-GYP Двоичный, он предварительно установлен в настоящее время рядом с узлом на каждой системе.

Это Узел-GYP Бинарный на самом деле, где все удобство жизни. Это очень Умельная утилита, которая знает, как генерировать системы сборки на кроссплатформе Основа, в зависимости от того, где она работает. Это на самом деле, где приходит его имя От: GYP для Генерировать ваши проекты И у него есть свои корни из GYP Проект команды хрома. Знает как Создание проекта Visual Studio на Windows и A сделать -Базированный процесс на Unix, но мы Получайте в деталях здесь, и я действительно хочу держать все просто.

Следующий важный бит – это japfile: правда Флаг в нашем Package.json файл. Это указывает на то, что Узел-GYP должен взять Binding.gyp Файл, который мы уже создан во внимание. Вот что мы собираемся заполнить этот файл с участием:

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

Здесь мы указываем, что мы намерены генерировать Native.node Модуль и это должны быть результатом компиляции main.cpp Отказ

Вот что будет достаточно для нашего примера на стороне C ++ (поставить, что в Main.CPP ):

#include 

void HelloWorld(const v8::FunctionCallbackInfo& args)
{
  v8::Isolate* isolate = args.GetIsolate();
  auto message =
    v8::String::NewFromUtf8(isolate, "Hello from the native side!");
  args.GetReturnValue().Set(message);
}

void Initialize(v8::Local exports)
{
  NODE_SET_METHOD(exports, "helloWorld", HelloWorld);
}

NODE_MODULE(module_name, Initialize)

Это будет выглядеть очень знакомо, если у вас есть какой-либо уровень владения с C ++. Здесь Мы определяем функцию имени HellowOrld это просто возвращает строку. Далее мы объявить HellowOrld Собственность на Экспорт объект, чтобы иметь значение HellowOrld Отказ Это эффективно приводит к модулю, которое экспортирует функцию, который возвращает базовую строку. Это эквивалентный код JS:

function HelloWorld() {
  return 'Hello from the native side!'
}

module.exports.helloWorld = HelloWorld

Теперь у нас есть работа в компиляции этого бита кода в Native.node файл.

npm install
ls build/Release

Вы можете увидеть, что это сгенерировало ./build/release/native.node Файл, который является Модуль ждут нас требовать и использовать его!

Теперь мы пойдем вперед и используем этот модуль (поставьте это в Main.js ):

let native = require('./build/Release/native.node')

console.log(native.helloWorld())

Потому что Native.node Модуль уже скомпилируется, мы можем безопасно запустить main.js Файл и просмотреть его запустить:

node main.js
Hello from the native side!

требуют (...) Часть выглядит немного уродливым, но мы можем очень легко исправить это с Помощь очень маленького модуля NPM называется Привязки Отказ

npm install bindings

И используйте модуль прямо сейчас. Вот в результате ?| main.js файл:

let native = require('bindings')('native')
console.log(native.helloWorld())

Намного проще и не нужно вручную проследить путь к Native.node файл! Привязки сделает тяжелый подъем для нас.

Немного более сложный пример

Затем мы собираемся выполнить вычисление, которое немного больше участвует на стороне C ++, просто чтобы доказать, что мы направляемся в правильное направление.

Мы собираемся создать функцию в C ++, которая принимает переменную сумму Аргументы и распечатайте их, используя знаменитые printf () функция. Хитрость – это Передайте только номера от JavaScript и выведите каждое число в столько базовых систем насколько это возможно. Мы собираемся обращаться с 2, 6, 7, 8 и 16 базами. Это будет Достаточно для нас, чтобы получить достаточно опасного.

Структура папки, которую мы собираемся использовать:

├── binding.gyp
├── build
│   ├── Makefile
│   ├── Release
│   ├── binding.Makefile
│   ├── config.gypi
│   ├── gyp-mac-tool
│   └── native.target.mk
├── main.cpp # C++ code for actually outputting formatted strings
├── main.js # the JS source code for running the program
└── package.json

4 directories, 11 files

Вот фактический код C ++, который реализует логику для преобразования номеров:

void NativePrintf(const v8::FunctionCallbackInfo& args)
{
    int number = (int) args[0]->NumberValue();
    std::cout << "Base 10: ";
    convertDecimalToOtherBase(number, 10);
    std::cout << std::endl;
    std::cout << "Base 2: ";
    convertDecimalToOtherBase(number, 2);
    std::cout << std::endl;
    std::cout << "Base 6: ";
    convertDecimalToOtherBase(number, 6);
    std::cout << std::endl;
    std::cout << "Base 7: ";
    convertDecimalToOtherBase(number, 7);
    std::cout << std::endl;
    std::cout << "Base 8: ";
    convertDecimalToOtherBase(number, 8);
    std::cout << std::endl;
    std::cout << "Base 16: ";
    convertDecimalToOtherBase(number, 16);
    std::cout << std::endl;
    std::cout << "-------------";
    std::cout << std::endl;
}

Функция ConvertDecimaltootherbase () опущен для краткости.

Полный исходный код для примера можно найти на Github Repository Отказ

Как видите, с небольшим количеством помощи от C ++ вы можете добиться довольно сложных вещей очень легко. Вы можете реализовать сложные приложения, которые запускают трубы или FIFO и встраивают их полностью в ваше существующее приложение узла, или вы можете использовать популярные сетевые библиотеки для C ++ в вашу программу небольшого узла. Воображение – это предел.