一个快速的谷歌搜索产生了至少一个为 node.js 编写C++“Hello World”的教程,但目前还不清楚是否可以只使用 C 来编写这样的扩展。假设有可能,我会面临哪些挑战/限制?
6 回答
如果您愿意,您可以用 C 编写部分扩展,但您至少需要一小部分 C++ 代码来将您的 C 代码与 Node.js 粘合在一起。
正如您将在 HelloWorld 中看到的那样,扩展依赖于v8.h
和node.h
标头,它们具有 Node 期望的所有类。没有这些,您将无法正确创建 JS 对象以导出回 Node。
也就是说,您可以很容易地编写一小组只调用 C 函数的 C++ 函数,并包装某种 C 结构。
在黑客新闻上找到了这个:
https://github.com/wesolows/v8plus
v8+:节点插件 C++ 到 C 边界
这一层提供了一种用 C 语言编写至少简单的 Node 插件的方法,而无需使用所有可怕的 C++ goop。那个 goop 仍然存在,但你不必写它。更重要的是,您可以在一个健全的编程环境中编写您的模块,避免令人困惑和容易出错的 C++ 语义。
现在我们至少有 3 个不错的选择:
node-ffi: Node.js 外部函数接口
插件,用于使用纯 JavaScript 加载和调用动态库。它可用于创建与本机库的绑定,而无需编写任何 C 代码
https://github.com/node-ffi/node-ffi
SWIG:简化的包装器和接口生成器
(它为多种语言生成包装器,一次解决了许多问题)
http://www.swig.org/
emscripten 将
C 和 C++ 编译成高度可优化的 JavaScript,即使在 Web 上也能以接近本机的速度运行,无需插件。
http://kripken.github.io/emscripten-site/
需要使用 extern "C" 语法在 C++ 代码中声明单个 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);
}
然后从 C++ 函数调用该函数:
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的示例代码
如果您的模块使用 libuv,您可以将其链接到节点可执行文件。它将 libuv 函数导出为共享库。
然后你可以使用 node-ffi 与之交互(这里不需要 C++ 知识)。
这是我在 Windows 上使用 MSVS 的方法:
- 在 MSVS 中创建新的 DLL 解决方案
- 下载libuv并将 include 和 lib 文件复制到 MSVS
- 下载node.lib文件,放到MSVS的lib文件夹下
- 编译下面的示例源代码,将计时器添加到主事件循环
测试库.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 中有网络、工作线程和其他选项...