Addons

Addons插件就是动态连接库。Addons插件将Node与C库和C++库链接起来。它的API(目前来说)相当复杂,涉及到了几个类库的知识。

  • V8 JavaScript引擎是一个 C++ 类库.。用于和JavaScript进行交互的接口。创建对象, 调用函数等. 文档大部分在这里:v8.h 头文件 (deps/v8/include/v8.h在Node源代码目录里), 也有可用的线上文档线上。
  • libuv, C语言编写的事件循环类库。任何时候需要等待一个文件描述符变为可读状态,等待一个定时器,或者等待一个接受信号都需要使用libuv类库的接口。也就是说,如果你执行任何I/O操作,libuv类库将会被用到。
  • 内部 Node 类库.最重要的接口就是 node::ObjectWrap 类,这个类你应该是最可能想要派生的。
  • 其他.请参阅 deps/ 获得更多可用类库。

Node 静态编译了所有依赖到它的可执行文件中去了。当编译你的模块时,你不必担心无法连接上述那些类库。

下面所有的例子都可以下载到:下载 这或许能成为你学习和创作自己addon插件的起点。

Hello world

作为开始,让我们用编写一个小的addon插件,这个addon插件的c++代码相当于下面的JavaScript代码。

module.exports.hello = function() { return 'world'; };

首先我们创建一个 hello.cc文件:

#include <node.h>
#include <v8.h>using namespace v8;Handle<Value> Method(const Arguments& args) {HandleScope scope;return scope.Close(String::New("world"));
}void init(Handle<Object> exports) {exports->Set(String::NewSymbol("hello"),FunctionTemplate::New(Method)->GetFunction());
}NODE_MODULE(hello, init)

注意所有Node的addons插件都必须输出一个初始化函数:

void Initialize (Handle<Object> exports);
NODE_MODULE(module_name, Initialize)

NODE_MODULE之后没有分号,因为它不是一个函数(请参阅 node.h

这个module_name(模块名)需要和最后编译生成的2进制文件名(减去.node后缀名)相同。

源代码需要生成在hello.node,这个2进制addon插件中。需要做到这些,我们要创建一个名为binding.gyp的文件,它描述了创建这个模块的配置,并且它的格式是类似JSON的。文件将被命令:node-gyp 编译。

{"targets": [{"target_name": "hello","sources": [ "hello.cc" ]}]
}

下一步是根据当前的操作系统平台,利用 node-gyp configure命令,生成合适的项目文件。

现在你会有一个Makefile (在Unix平台) 或者一个 vcxproj file(在Windows上),它们都在build/ 文件夹中. 然后执行命令 node-gyp build进行编译。

现在你已经有了编译好的 .node 文件了,这个编译好的绑定文件会在目录 build/Release/

现在你可以使用这个2进制addon插件在Node项目 hello.js 中了,通过指明 require这个刚刚创建的 hello.node模块使用它。

var addon = require('./build/Release/hello');console.log(addon.hello()); // 'world'

请阅读下面的内容获得更多详情或者访问 https://github.com/arturadib/node-qt获取一个生产环境的例子。

Addon patterns(插件方式)

下面是一些帮助你开始编写addon插件的方式。参考这个在线的 v8 手册用来帮助你调用各种v8接口, 然后是v8的 嵌入式开发向导 ,解释几个概念,如 handles, scopes,function templates等。

为了能跑起来这些例子,你必须用 node-gyp 来编译他们。创建一个binding.gyp 文件:

{"targets": [{"target_name": "addon","sources": [ "addon.cc" ]}]
}

事实上可以有多个 .cc 文件, 就简单的在 sources 数组里加上即可,示例:

"sources": ["addon.cc", "myexample.cc"]

现在你有了你的binding.gyp文件了,你可要开始执行configure 和 build 命令构建你的addon插件了

$ node-gyp configure build

Function arguments(函数参数)

下面的部分说明了如何从JavaScript的函数调用读取参数然后返回一个值。这是主要的内容并且仅需要源代码 addon.cc

#define BUILDING_NODE_EXTENSION
#include <node.h>using namespace v8;Handle<Value> Add(const Arguments& args) {HandleScope scope;if (args.Length() < 2) {ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));return scope.Close(Undefined());}if (!args[0]->IsNumber() || !args[1]->IsNumber()) {ThrowException(Exception::TypeError(String::New("Wrong arguments")));return scope.Close(Undefined());}Local<Number> num = Number::New(args[0]->NumberValue() +args[1]->NumberValue());return scope.Close(num);
}void Init(Handle<Object> exports) {exports->Set(String::NewSymbol("add"),FunctionTemplate::New(Add)->GetFunction());
}NODE_MODULE(addon, Init)

你可以使用下面的JavaScript代码来测试它

var addon = require('./build/Release/addon');console.log( 'This should be eight:', addon.add(3,5) );

Callbacks(回调)

你可以传递JavaScript functions 到一个C++ function 并且执行他们,这里是 addon.cc文件:

#define BUILDING_NODE_EXTENSION
#include <node.h>using namespace v8;Handle<Value> RunCallback(const Arguments& args) {HandleScope scope;Local<Function> cb = Local<Function>::Cast(args[0]);const unsigned argc = 1;Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) };cb->Call(Context::GetCurrent()->Global(), argc, argv);return scope.Close(Undefined());
}void Init(Handle<Object> exports, Handle<Object> module) {module->Set(String::NewSymbol("exports"),FunctionTemplate::New(RunCallback)->GetFunction());
}NODE_MODULE(addon, Init)

注意这个例子对 Init()使用了两个参数,将完整的 module 对象作为第二个参数传入。这允许addon插件完全的重写 exports,这样就可以用一个函数代替多个函数作为 exports的属性了。

你可以使用下面的JavaScript代码来测试它:

var addon = require('./build/Release/addon');addon(function(msg){console.log(msg); // 'hello world'
});

Object factory(对象工厂)

在这个addon.cc文件里用一个c++函数,你可以创建并且返回一个新的对象,这个新的对象拥有一个msg的属性,它的值是通过createObject()方法传入的

#define BUILDING_NODE_EXTENSION
#include <node.h>using namespace v8;Handle<Value> CreateObject(const Arguments& args) {HandleScope scope;Local<Object> obj = Object::New();obj->Set(String::NewSymbol("msg"), args[0]->ToString());return scope.Close(obj);
}void Init(Handle<Object> exports, Handle<Object> module) {module->Set(String::NewSymbol("exports"),FunctionTemplate::New(CreateObject)->GetFunction());
}NODE_MODULE(addon, Init)

在js中测试如下:

var addon = require('./build/Release/addon');var obj1 = addon('hello');
var obj2 = addon('world');
console.log(obj1.msg+' '+obj2.msg); // 'hello world'

Function factory(函数工厂)

这次将展示如何创建并返回一个JavaScript function函数,这个函数其实是通过c++包装的。

#define BUILDING_NODE_EXTENSION
#include <node.h>using namespace v8;Handle<Value> MyFunction(const Arguments& args) {HandleScope scope;return scope.Close(String::New("hello world"));
}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 anonymousreturn scope.Close(fn);
}void Init(Handle<Object> exports, Handle<Object> module) {module->Set(String::NewSymbol("exports"),FunctionTemplate::New(CreateFunction)->GetFunction());
}NODE_MODULE(addon, Init)

测试:

var addon = require('./build/Release/addon');var fn = addon();
console.log(fn()); // 'hello world'

Wrapping C++ objects(包装c++对象)

这里将创建一个被c++包裹的对象或类 MyObject,它是可以在JavaScript中通过 new操作符实例化的。首先我们要准备主要的模块文件 addon.cc

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"using namespace v8;void InitAll(Handle<Object> exports) {MyObject::Init(exports);
}NODE_MODULE(addon, InitAll)

然后在 myobject.h文件中创建继承自 node::ObjectWrap的包装类 :

#ifndef MYOBJECT_H
#define MYOBJECT_H#include <node.h>class MyObject : public node::ObjectWrap {public:static void Init(v8::Handle<v8::Object> exports);private:explicit MyObject(double value = 0);~MyObject();static v8::Handle<v8::Value> New(const v8::Arguments& args);static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args);static v8::Persistent<v8::Function> constructor;double value_;
};#endif

在文件 myobject.cc 可以实现各种你想要显示的方法。 这里我们通过将方法添加到构造函数的原型中来显示方法名为 plusOne的函数。

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"using namespace v8;Persistent<Function> MyObject::constructor;MyObject::MyObject(double value) : value_(value) {}MyObject::~MyObject() {}void MyObject::Init(Handle<Object> exports) {// Prepare constructor templateLocal<FunctionTemplate> tpl = FunctionTemplate::New(New);tpl->SetClassName(String::NewSymbol("MyObject"));tpl->InstanceTemplate()->SetInternalFieldCount(1);// Prototypetpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),FunctionTemplate::New(PlusOne)->GetFunction());constructor = Persistent<Function>::New(tpl->GetFunction());exports->Set(String::NewSymbol("MyObject"), constructor);
}Handle<Value> MyObject::New(const Arguments& args) {HandleScope scope;if (args.IsConstructCall()) {// Invoked as constructor: `new MyObject(...)`double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();MyObject* obj = new MyObject(value);obj->Wrap(args.This());return args.This();} else {// Invoked as plain function `MyObject(...)`, turn into construct call.const int argc = 1;Local<Value> argv[argc] = { args[0] };return scope.Close(constructor->NewInstance(argc, argv));}
}Handle<Value> MyObject::PlusOne(const Arguments& args) {HandleScope scope;MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());obj->value_ += 1;return scope.Close(Number::New(obj->value_));
}

使用以下示例测试:

var addon = require('./build/Release/addon');var obj = new addon.MyObject(10);
console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13

Factory of wrapped objects(工厂包装对象)

当你想创建原生的JavaScript对象,又不想明确的使用JavaScript的new操作符时,这种模式是非常有用的。

var obj = addon.createObject();
// instead of:
// var obj = new addon.Object();

让我们注册在 addon.cc 文件中注册createObject方法:

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"using namespace v8;Handle<Value> CreateObject(const Arguments& args) {HandleScope scope;return scope.Close(MyObject::NewInstance(args));
}void InitAll(Handle<Object> exports, Handle<Object> module) {MyObject::Init();module->Set(String::NewSymbol("exports"),FunctionTemplate::New(CreateObject)->GetFunction());
}NODE_MODULE(addon, InitAll)

myobject.h文件中,我们现在引入了 能够实例化对象的静态方法NewInstance (它的工作就像是 在JavaScript中的new操作符。)

#define BUILDING_NODE_EXTENSION
#ifndef MYOBJECT_H
#define MYOBJECT_H#include <node.h>class MyObject : public node::ObjectWrap {public:static void Init();static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args);private:explicit MyObject(double value = 0);~MyObject();static v8::Handle<v8::Value> New(const v8::Arguments& args);static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args);static v8::Persistent<v8::Function> constructor;double value_;
};#endif

这里的实现模式与上面的 myobject.cc类似:

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"using namespace v8;Persistent<Function> MyObject::constructor;MyObject::MyObject(double value) : value_(value) {}MyObject::~MyObject() {}void MyObject::Init() {// Prepare constructor templateLocal<FunctionTemplate> tpl = FunctionTemplate::New(New);tpl->SetClassName(String::NewSymbol("MyObject"));tpl->InstanceTemplate()->SetInternalFieldCount(1);// Prototypetpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),FunctionTemplate::New(PlusOne)->GetFunction());constructor = Persistent<Function>::New(tpl->GetFunction());
}Handle<Value> MyObject::New(const Arguments& args) {HandleScope scope;if (args.IsConstructCall()) {// Invoked as constructor: `new MyObject(...)`double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();MyObject* obj = new MyObject(value);obj->Wrap(args.This());return args.This();} else {// Invoked as plain function `MyObject(...)`, turn into construct call.const int argc = 1;Local<Value> argv[argc] = { args[0] };return scope.Close(constructor->NewInstance(argc, argv));}
}Handle<Value> MyObject::NewInstance(const Arguments& args) {HandleScope scope;const unsigned argc = 1;Handle<Value> argv[argc] = { args[0] };Local<Object> instance = constructor->NewInstance(argc, argv);return scope.Close(instance);
}Handle<Value> MyObject::PlusOne(const Arguments& args) {HandleScope scope;MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());obj->value_ += 1;return scope.Close(Number::New(obj->value_));
}

测试:

var createObject = require('./build/Release/addon');var obj = createObject(10);
console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13var obj2 = createObject(20);
console.log( obj2.plusOne() ); // 21
console.log( obj2.plusOne() ); // 22
console.log( obj2.plusOne() ); // 23

Passing wrapped objects around(传递包装的对象)

除了包装和返回c++对象以外,你可以传递他们并且通过Node的 node::ObjectWrap::Unwrap帮助函数解包装他们。在下面的 addon.cc 文件中,我们介绍了一个函数 add(),它能够获取2个 MyObject对象。

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"using namespace v8;Handle<Value> CreateObject(const Arguments& args) {HandleScope scope;return scope.Close(MyObject::NewInstance(args));
}Handle<Value> Add(const Arguments& args) {HandleScope scope;MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>(args[0]->ToObject());MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>(args[1]->ToObject());double sum = obj1->Value() + obj2->Value();return scope.Close(Number::New(sum));
}void InitAll(Handle<Object> exports) {MyObject::Init();exports->Set(String::NewSymbol("createObject"),FunctionTemplate::New(CreateObject)->GetFunction());exports->Set(String::NewSymbol("add"),FunctionTemplate::New(Add)->GetFunction());
}NODE_MODULE(addon, InitAll)

为了使事情变得有趣,我们在 myobject.h 采用一个公共的方法,所以我们能够在unwrapping解包装对象之后使用私有成员的值。

#define BUILDING_NODE_EXTENSION
#ifndef MYOBJECT_H
#define MYOBJECT_H#include <node.h>class MyObject : public node::ObjectWrap {public:static void Init();static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args);double Value() const { return value_; }private:explicit MyObject(double value = 0);~MyObject();static v8::Handle<v8::Value> New(const v8::Arguments& args);static v8::Persistent<v8::Function> constructor;double value_;
};#endif

myobject.cc文件的实现模式前面类似:

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"using namespace v8;Persistent<Function> MyObject::constructor;MyObject::MyObject(double value) : value_(value) {}MyObject::~MyObject() {}void MyObject::Init() {// Prepare constructor templateLocal<FunctionTemplate> tpl = FunctionTemplate::New(New);tpl->SetClassName(String::NewSymbol("MyObject"));tpl->InstanceTemplate()->SetInternalFieldCount(1);constructor = Persistent<Function>::New(tpl->GetFunction());
}Handle<Value> MyObject::New(const Arguments& args) {HandleScope scope;if (args.IsConstructCall()) {// Invoked as constructor: `new MyObject(...)`double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();MyObject* obj = new MyObject(value);obj->Wrap(args.This());return args.This();} else {// Invoked as plain function `MyObject(...)`, turn into construct call.const int argc = 1;Local<Value> argv[argc] = { args[0] };return scope.Close(constructor->NewInstance(argc, argv));}
}Handle<Value> MyObject::NewInstance(const Arguments& args) {HandleScope scope;const unsigned argc = 1;Handle<Value> argv[argc] = { args[0] };Local<Object> instance = constructor->NewInstance(argc, argv);return scope.Close(instance);
}

测试:

var addon = require('./build/Release/addon');var obj1 = addon.createObject(10);
var obj2 = addon.createObject(20);
var result = addon.add(obj1, obj2);console.log(result); // 30

Node.js v0.10.31API手册-Addons插件相关推荐

  1. Node.js v0.10版本发布

    Node.js研发团队发布了node.js v0.10版本,它是个基于Javascript.用于构建高性能异步服务器的平台.该版本主要更新如下:更易于使用的数据流处理模块,通过域更好地处理错误,此外还 ...

  2. 通常,Node.js如何处理10,000个并发请求?

    本文翻译自:How, in general, does Node.js handle 10,000 concurrent requests? I understand that Node.js use ...

  3. Node.js 调用 C++ 方法 / C++ Addons 详解

    最近开发涉及到了一些Node.js调用C++的地方,于是网上搜了一下,发现网上好多文章都是比较片面的东西,没法直接使用.于是花点时间总结一下. Android开发中Java 调用C++的部分叫JNI, ...

  4. Node.js 应用故障排查手册 —— 综合性 GC 问题和优化

    楔子 本章前面两节生产案例分别侧重于单一的 CPU 高和单一的内存问题,我们也给大家详细展示了问题的定位排查过程,那么实际上还有一类相对更复杂的场景--它本质上是 V8 引擎的 GC 引发的问题. 简 ...

  5. Node.js 应用故障排查手册 —— 正确打开 Chrome devtools

    楔子 前面的预备章节中我们大致了解了如何在服务器上的 Node.js 应用出现问题时,从常规的错误日志.系统/进程指标以及兜底的核心转储这些角度来排查问题.这样就引出了下一个问题:我们知道进程的 CP ...

  6. Node.js 和npm的安装(插件的安装)

    一.node.js是什么?有什么用? 1.概述: a).Node.js是基于Chrome JavaScript运行时建立的一个平台,实际上它是对Google Chrome V8引擎进行了封装,它主要用 ...

  7. Node.js 应用故障排查手册 —— 雪崩型内存泄漏问题

    楔子 实践篇一中我们也看到了一个比较典型的由于开发者不当使用第三方库,而且在配置信息中携带了三方库本身使用不到的信息,导致了内存泄漏的案例,实际上类似这种相对缓慢的 Node.js 应用内存泄漏问题我 ...

  8. Node.js 应用故障排查手册 —— 类死循环导致进程阻塞

    类死循环导致进程阻塞 楔子 在实践篇一中我们看到了两个表象都是和 CPU 相关的生产问题,它们基本也是我们在线上可能遇到的这一类问题的典型案例,而实际上这两个案例也存在一个共同点:我们可以通过 Nod ...

  9. Node.js 应用故障排查手册 —— 大纲与常规问题指标简介

    楔子 你是否想要尝试进行 Node.js 应用开发但是又总听人说它不安全.稳定性差,想在公司推广扩张大前端的能力范畴和影响又说服不了技术领导. JavaScript 发展到今天,早已脱离原本浏览器的战 ...

最新文章

  1. 【PL/SQL】--导出oracle单表数据--drp204
  2. 颈椎病2句话就能治疗好,你也试试看,一学就会!
  3. v-show 与 v-if 的区别
  4. 深入理解yield from语法
  5. python dlib学习(一):人脸检测
  6. Linux 磁盘管理命令
  7. (线性基) bzoj 2460
  8. ThreadLocal和线程同步机制的对比
  9. 字节跳动实验室招聘户型图理解实习生|北京
  10. 应用机器学习视频教程,哥伦比亚大学 2020版
  11. php redis 用户会话,使用Redis保存用户会话Session详解
  12. ant中修改a-switch的大小、修改a-checkbox的大小
  13. FPGA 之 SOPC 系列(五)Nios II 软件使用与程序开发 I
  14. 解决安装Steam提示steam需要在线更新问题
  15. 大学计算机应用作业,大学计算机应用作业
  16. 计算机硬件的组装实践,论文-计算机硬件组装实践.doc
  17. 微信小问题2021-10-14
  18. java备忘--20190828
  19. 微信是胖客户端瘦服务器,一款管理微信客户软件的系统体系结构设计?
  20. 握手定理(握手数之和为偶数)和相关2个推论

热门文章

  1. MOSFET的SOA或者ASO是什么?
  2. 可信、安全、稳定构建金融科技新局面
  3. IntelliJ IDEA远程调试设置
  4. html parser java库_Java解析HTML之HTMLParser使用与详解
  5. Linux设备驱动---OMAP3630 Linux I2C总线驱动分析(2)
  6. OMAP3630 Linux I2C总线驱动分析(2)
  7. 【MyBatis-Plus】CRUD 操作
  8. springboot 分组校验和顺序校验
  9. mysql命令行的使用
  10. 欧氏距离详解及在matlab中的实现