这篇文章主要介绍了使用Node.js为其他程序编写扩展的基本方法 ,文中示例是通过Node让JavaScript代码与C++应用产生交互,需要的朋友可以参考下
准备开始
首先我们用下面的目录结构来创建一个节点通知(node-notify)文件夹.
代码如下:
.
|-- build/ # This is where our extension is built.
|-- demo/
| `-- demo.js # This is a demo Node.js script to test our extension.
|-- src/
| `-- node_gtknotify.cpp # This is the where we do the mapping from C++ to Javascript.
`-- wscript # This is our build configuration used by node-waf
这个看起来很漂亮的tree 用通用的 tree 生成.
现在让我来创建测试脚本demo.js 和决定我们扩展的API前期看起来应该像:
?
1
2
3
4
5
6
7
8// This loads our extension on the notify variable.
// It will only load a constructor function, notify.notification().
var notify = require("../build/default/gtknotify.node"); // path to our extension
var notification = new notify.notification();
notification.title = "Notification title";
notification.icon = "emblem-default"; // see /usr/share/icons/gnome/16x16
notification.send("Notification message");
编写我们的Node.js扩展
Init方法
为了创建一个Node.js扩展,我们需要编写一个继承node::ObjectWrap的C++类。 ObjectWrap 实现了让我们更容易与Javascript交互的公共方法
我们先来编写类的基本框架:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37#include
#include
// We will need the following libraries for our GTK+ notification
#include
#include
#include
using namespace v8;
class Gtknotify : node::ObjectWrap {
private:
public:
Gtknotify() {}
~Gtknotify() {}
static void Init(Handle target) {
// This is what Node will call when we load the extension through require(), see boilerplate code below.
}
};
/*
* WARNING: Boilerplate code ahead.
*
* See https://www.cloudkick.com/blog/2010/aug/23/writing-nodejs-native-extensions/ & http://www.freebsd.org/cgi/man.cgi?query=dlsym
*
* Thats it for actual interfacing with v8, finally we need to let Node.js know how to dynamically load our code.
* Because a Node.js extension can be loaded at runtime from a shared object, we need a symbol that the dlsym function can find,
* so we do the following:
*/
v8::Persistent
extern "C" { // Cause of name mangling in C++, we use extern C here
static void init(Handle target) {
Gtknotify::Init(target);
}
// @see http://github.com/ry/node/blob/v0.2.0/src/node.h#L101
NODE_MODULE(gtknotify, init);
}
现在,我们必须把下面的代码编写到我们的Init()方法中:
声明构造函数,并将其绑定到我们的目标变量。var n = require("notification");将绑定notification() 到 n:n.notification().
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14// Wrap our C++ New() method so that it's accessible from Javascript
// This will be called by the new operator in Javascript, for example: new notification();
v8::Local
// Make it persistent and assign it to persistent_function_template which is a static attribute of our class.
Gtknotify::persistent_function_template = v8::Persistent
// Each JavaScript object keeps a reference to the C++ object for which it is a wrapper with an internal field.
Gtknotify::persistent_function_template->InstanceTemplate()->SetInternalFieldCount(1); // 1 since a constructor function only references 1 object
// Set a "class" name for objects created with our constructor
Gtknotify::persistent_function_template->SetClassName(v8::String::NewSymbol("Notification"));
// Set the "notification" property of our target variable and assign it to our constructor function
target->Set(String::NewSymbol("notification"), Gtknotify::persistent_function_template->GetFunction());
声明属性:n.title 和n.icon.
?
1
2
3
4
5// Set property accessors
// SetAccessor arguments: Javascript property name, C++ method that will act as the getter, C++ method that will act as the setter
Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("title"), GetTitle, SetTitle);
Gtknotify::persistent_function_template->InstanceTemplate()->SetAccessor(String::New("icon"), GetIcon, SetIcon);
// For instance, n.title = "foo" will now call SetTitle("foo"), n.title will now call GetTitle()
声明原型方法:n.send()
?
1
2
3// This is a Node macro to help bind C++ methods to Javascript methods (see https://github.com/joyent/node/blob/v0.2.0/src/node.h#L34)
// Arguments: our constructor function, Javascript method name, C++ method name
NODE_SET_PROTOTYPE_METHOD(Gtknotify::persistent_function_template, "send", Send);
现在我们的Init()方法看起来应该是这样的:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// Our constructor
static v8::Persistent
static void Init(Handle target) {
v8::HandleScope scope; // used by v8 for garbage collection
// Our constructor
v8::Local
Gtknotify::persistent_function_template = v8::Persistent
Gtknotify::persistent_function_template->InstanceTemplate()->SetInternalFieldCount(1); // 1 since this is a constructor function
Gtknotify::persistent_function_template->SetClassName(v8::String::NewSymbol("Notification"));
// Our getters and setters
Gtknotify