Во время работы встала задача научиться писать интерфейсы к C/C++ функциям для Node.JS. Эта задача становится всё более актуальной в свете того, что основные камни летят в node именно из-за нехватки некоторого привычного для разработчиков функционала (ничего они не понимают в жизни).
В данной статье я хотел бы немного описать своё видение платформы Node.JS, а также поговорить немного о том, как писать модули к Node на C++ .
Сразу оговорюсь: я не профессиональный web-разработчик, никогда в жизни профессионально не занимался даже бразуерным JavaScript-ом до недавнего времени, когда ко мне обратились примерно со следующей просьбой: "Узнай, что вообще такое этот node и попробуй прикрутить туда какую-нибудь интересную штуку, например мои любимые linux-библиотеки на C". Было бы знание, да незнание помогло. Порой труднее оказывается объяснить, что такое серверный JavaScript людям, для которых DOM - родной дом.
На первый взгляд node действительно оказывается привлекательным для работы с сокетами, портами и вообще всем, что нужно время от времени опрашивать и периодически формировать на это какие-либо ответы. Его однопоточной модели с Event Loop оказывается вполне достаточно для проектирования приложений в рамках своих "родных" модулей, то есть философия данной платформы является вполне оправданной и самодостаточной. Один из самых полных обзоров node на языке Пушкина http://fprog.ru/2010/issue6/dmitry-demeshchuk-node.js-vs-erlang/ изложен именно в ключе сравнения с языком Erlang, который на сегодня является одной из самых эффективных платформ для создания коммуникационных узлов в сети.
"Ну а как же тонны уже написанного кода, а также инструменты, без которых нельзя?"
Этим, собственно, сейчас и приходит ся заниматься сообществу node.js-разработчиков.
Первую статью хотелось бы посвятить обзору важных составляющих движка V8, на котором, собственно, и написан node.js и благодаря которому в Chrome и в Node так быстро обрабатывается JavaScript.
Первое и самое важное, что нам необходимо для нашей задачи - заголовочный файл v8.h или его Doxygen представление (например http://bespin.cz/~ondras/html/).
Основной элемент представления в V8 - Handle, который представляет нам любую структурную единицу языка JavaScript, а также взаимодействует со сборщиком мусора V8.
Все эти элементы существуют в определённом scope, который мы явно определяем.
Через определённые промежутки времени сборщик мусора V8 занимается удалением из памяти handle-ов, на которые больше никто не ссылается, таким образом освобождая место в куче для новых объектов JavaScript.
Правда тут есть одно исключение - помимо обычных handle-ов (локальных, Local) существуют также и постоянные, которые находятся вне ведения сборщика мусора (Persistent).
Каждый Handle может содержать в себе некоторый JavaScript-объект.
Структура объектов JavaScript в представлении V8 имеет следующий вид:
В данной статье я хотел бы немного описать своё видение платформы Node.JS, а также поговорить немного о том, как писать модули к Node на C++ .
Сразу оговорюсь: я не профессиональный web-разработчик, никогда в жизни профессионально не занимался даже бразуерным JavaScript-ом до недавнего времени, когда ко мне обратились примерно со следующей просьбой: "Узнай, что вообще такое этот node и попробуй прикрутить туда какую-нибудь интересную штуку, например мои любимые linux-библиотеки на C". Было бы знание, да незнание помогло. Порой труднее оказывается объяснить, что такое серверный JavaScript людям, для которых DOM - родной дом.
На первый взгляд node действительно оказывается привлекательным для работы с сокетами, портами и вообще всем, что нужно время от времени опрашивать и периодически формировать на это какие-либо ответы. Его однопоточной модели с Event Loop оказывается вполне достаточно для проектирования приложений в рамках своих "родных" модулей, то есть философия данной платформы является вполне оправданной и самодостаточной. Один из самых полных обзоров node на языке Пушкина http://fprog.ru/2010/issue6/dmitry-demeshchuk-node.js-vs-erlang/ изложен именно в ключе сравнения с языком Erlang, который на сегодня является одной из самых эффективных платформ для создания коммуникационных узлов в сети.
"Ну а как же тонны уже написанного кода, а также инструменты, без которых нельзя?"
Этим, собственно, сейчас и приходит ся заниматься сообществу node.js-разработчиков.
Первую статью хотелось бы посвятить обзору важных составляющих движка V8, на котором, собственно, и написан node.js и благодаря которому в Chrome и в Node так быстро обрабатывается JavaScript.
Первое и самое важное, что нам необходимо для нашей задачи - заголовочный файл v8.h или его Doxygen представление (например http://bespin.cz/~ondras/html/).
Основной элемент представления в V8 - Handle, который представляет нам любую структурную единицу языка JavaScript, а также взаимодействует со сборщиком мусора V8.
Все эти элементы существуют в определённом scope, который мы явно определяем.
Через определённые промежутки времени сборщик мусора V8 занимается удалением из памяти handle-ов, на которые больше никто не ссылается, таким образом освобождая место в куче для новых объектов JavaScript.
Правда тут есть одно исключение - помимо обычных handle-ов (локальных, Local) существуют также и постоянные, которые находятся вне ведения сборщика мусора (Persistent).
Каждый Handle может содержать в себе некоторый JavaScript-объект.
Структура объектов JavaScript в представлении V8 имеет следующий вид:
Значения, которые мы передаём в наш модуль из JavaScript - как правило, Value и должны быть явно преобразованы к тому типу, который мы ожидаем с помощью встроенных в V8 преобразователей типов.
Раз мы пишем модуль, значит нам нужно представить в нём определённый набор функций, которые будут выполнять C/C++ код, но при этом могут быть вызваны из JavaScript (что-то вроде FFI).
Начнём с очень простого. Модуль, написанный нами на C++ и запрашиваемый в Node (require('./our_module')) есть не что иное, как обычный JavaScript-объект. Задача, которая стоит перед нами - проста: описать необходимые поля этого модуля. В случае, когда мы имеем библиотеку функций на C удобно представить поля нашего объекта, как набор функций - просто чтобы не мучаться с архитектурой и не городить новые сущности.
#include <stdio.h> #include <node.h> using namespace v8; static Handle<Value> ExampleFunction(const Arguments &args) { printf("DEBUG:: This is our example function\n"); } extern "C" void init (Handle<Object> target) { HandleScope scope; target->Set(String::NewSymbol("ExampleFunction"), FunctionTemplate::New(ExampleFunction)->GetFunction()); }
Для того, чтобы собрать наш модуль, редактируем файл wscript:
srcdir = '' blddir = 'lib' VERSION = '0.0.1' def set_options(opt): opt.tool_options('compiler_cxx') def configure(conf): conf.check_tool('compiler_cxx') conf.check_tool('node_addon') conf.env.append_unique('CXXFLAGS', ["-lpthread"]) def build(bld): obj = bld.new_task_gen('cxx', 'shlib', 'node_addon') obj.target = 'sample' obj.source = './binding.cc'Собираем и тестируем:
В следующий раз поговорим о том, как преобразовывать аргументы функции и возвращаемые параметры, а также о блокирующих функциях, коллбэках и их конвертации в JavaScript.
Ссылки:
http://code.google.com/apis/v8/embed.html
http://howtonode.org/how-to-module
http://fprog.ru/2010/issue6/dmitry-demeshchuk-node.js-vs-erlang/