Быстрый поиск в Google дает по крайней мере один учебник для написания С++ "Hello World" для node.js, но неясно, можно ли написать такую расширение, использующее только C. Предполагая, что с какими проблемами/ограничениями я сталкиваюсь?
Возможно ли написать расширение node.js в C (не С++)?
Ответ 1
Вы можете написать части своего расширения в C, если хотите, но вам понадобится хотя бы небольшой код С++ для склеивания кода C с помощью Node.
Как вы видели в своем HelloWorld, расширения полагаются на заголовки v8.h
и node.h
, которые имеют все классы, которые ожидает Node. Без них вы не сможете правильно создать объект JS для экспорта обратно в Node.
Тем не менее, вы можете просто написать небольшой набор функций С++, которые просто вызывают функции C, и обернуть какую-то структуру C.
Ответ 2
Найдено в Hacker News:
https://github.com/wesolows/v8plus
v8 +: Node аддон С++ до границы C
Этот слой предлагает способ записи, по крайней мере, простых Node аддонов в C без всякого ужасного С++ goop, которого вы в противном случае ожидали использовать. Этот goop все еще существует, но вам не нужно его писать. Что еще более важно, вы можете написать свой модуль в разумной среде программирования, избегая запутанной и подверженной ошибкам семантики С++.
Ответ 3
Необходимо объявить отдельную функцию C в коде С++ с использованием синтаксиса extern "C"
Пример:
#define BUILDING_NODE_EXTENSION
#include <node.h>
extern "C" void f(int i, char c, float x);
using namespace v8;
если у вас есть несколько функций C, тогда его можно сгруппировать по фигурным скобкам:
extern "C" {
void f(int i, char c, float x);
int g(char* s, char const* s2);
double sqrtOfSumOfSquares(double a, double b);
}
затем вызовите функцию из функции С++:
Handle<Value> MyFunction(const Arguments& args) {
HandleScope scope;
f(7, 'x', 3.14); // <---
return scope.Close(String::New("Hello"));
}
Handle<Value> CreateFunction(const Arguments& args) {
HandleScope scope;
Local<FunctionTemplate> tpl = FunctionTemplate::New(MyFunction);
Local<Function> fn = tpl->GetFunction();
fn->SetName(String::NewSymbol("theFunction")); // omit this to make it anonymous
return scope.Close(fn);
}
void Init(Handle<Object> target) {
target->Set(String::NewSymbol("createFunction"),
FunctionTemplate::New(CreateFunction)->GetFunction());
}
NODE_MODULE(addon, Init)
Примечание. Использование примера кода из Nodejs Addons
Ответ 4
Теперь у нас есть как минимум 3 хорошие варианты:
node -ffi: Node.js Интерфейс внешних функций
аддон для загрузки и вызова динамических библиотек с использованием чистого JavaScript. Его можно использовать для создания привязок к родным библиотекам без написания кода C
https://github.com/node-ffi/node-ffi
SWIG: Упрощенный Wrapper и Interface Generator
(он создает обертки для многих языков, что одновременно решает многие проблемы)
http://www.swig.org/
emscripten
Компилирует C и С++ в высоко оптимизируемый JavaScript, который работает даже в Интернете с близкой скоростью, без плагинов.
http://kripken.github.io/emscripten-site/
Ответ 5
Код, который напрямую взаимодействует с node.js, должен быть написан на С++.
Вы можете написать обертки extern "C"
, используя непрозрачные типы для всего, что вам нужно от node.h и v8.h, но, вероятно, проще просто использовать С++ вместо этого (что, конечно же, может вызвать код C).
Ответ 6
Если ваш модуль использует libuv, вы можете связать его с исполняемым файлом node. Он экспортирует функции libuv в качестве общей библиотеки.
Затем вы можете использовать node -ffi для взаимодействия с ним (здесь не требуется знание С++).
Вот как я сделал это в Windows, используя MSVS:
- Создайте новое решение для DLL в MSVS
- Загрузите libuv и скопируйте файлы include и lib в MSVS
- Загрузите node.lib файл и поместите его в папку lib из MSVS
- Скомпилируйте приведенный ниже пример источника, который добавляет таймер в цикл основного события
testlib.c:
#include <stdio.h>
#include <stdlib.h>
#include "uv.h"
void (*p_callback)(int number, char *text);
void timer_cb1 (uv_timer_t* timer, int status) {
printf("libuv timer here\n", status);
p_callback(123, "it worked!");
}
void set_timer (int interval, void *pfunction) {
uv_loop_t *loop;
uv_timer_t *timer1;
printf("set_timer called. interval=%d callback=%p\n", interval, pfunction);
p_callback = pfunction;
printf("uv_version_string = %s\n", uv_version_string());
loop = uv_default_loop();
if (loop == 0) {
puts("could not get the reference to the default loop");
return;
}
puts("got the default loop. now allocating the timer struct");
timer1 = (uv_timer_t *) malloc(sizeof(uv_timer_t));
if (timer1 == 0) {
puts("malloc failed");
return;
}
puts("initializing timer");
uv_timer_init(loop, timer1);
puts("starting timer");
uv_timer_start(timer1, (uv_timer_cb) &timer_cb1, interval, interval);
puts("timer created. returning");
}
используйте testlib.def:
EXPORTS set_timer
И не забудьте ссылаться на node.lib
- Переместите созданную dll в тестовую папку и запустите там следующие команды:
npm install ffi
(в настоящее время требуются инструменты сборки, проверьте инструкции)
node test-lib.js
test-lib.js находится здесь:
var ffi = require('ffi');
var testlib = ffi.Library('testlib', {
'set_timer': [ 'void', [ 'int', 'pointer' ] ]
});
var callback = ffi.Callback('void', ['int', 'string'],
function(number, text) {
console.log("javascript callback here!!! number=" + number + " text=" + text);
}
);
console.log('registering the callback...');
testlib.set_timer(500, callback);
console.log('done')
Используйте свое воображение. У нас есть сети, рабочие потоки и другие варианты внутри libuv...