C++写node笔记(二)

2012-11-24 01:09

C++写node笔记(二)

by snoopyxdy

at 2012-11-23 17:09:47

original http://snoopyxdy.blog.163.com/blog/static/60117440201210233473129

上一学习笔记我们了解到了如何简单的编写一个c++的node模块,由于是新手,于是我就对照着node.js的api上的例子,再参考V8的接口文档,摸索着深入了解如何灵活编写c++的node模块,V8真的很好玩,接口写的很简单,调用方便,感觉已经陷进去了。


我们先简单解释一下简单的hello world的例子:
#include <node.h>

#include <v8.h>

using namespace v8; //使用命名空间v8,不用在代码中写v8::XXXX

Handle<Value> Method(const Arguments& args) { //定义一个function,返回Handle<Value>类的值
HandleScope scope; //实例化scope,用来存放返回值
return scope.Close(String::New("world")); //将返回值丢入scope.close中
}

void init(Handle<Object> target) { //接受参数 Handle<Object>类型,注意,这里target是指针
target->Set(String::NewSymbol("hello"), //调用Set方法,对这个对象增加key和value

FunctionTemplate::New(Method)->GetFunction()); //调用类FunctionTemplate的静态成员New方法,传入Handle<Value>类型的Method,然后调用GetFunction方法生成node函数,具体v8参考手册上有说明

}
NODE_MODULE(hello, init) //固定将文件名hello,和init初始化函数丢入NODE_MODULE

官网有这段话:所有的node的插件必须输出一个初始化的函数,也就是说如下代码是在每个模块都必须有的,固定格式。

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


接下来我们看官网的第二段例子:
官网的第二段例子是调用add方法可以实现2个整数相加
 

#define BUILDING_NODE_EXTENSION
#include <node.h>

using namespace v8;

Handle<Value> Add(const Arguments& args) {
HandleScope scope;

if (args.Length() < 2) { //如果args的长度小于2,则抛出错误,从下面代码我们知道如何抛出一个错误,以后照搬就行
ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
return scope.Close(Undefined());//返回Udefined
}

if (!args[0]->IsNumber() || !args[1]->IsNumber()) {//判断2个参数是否是number
ThrowException(Exception::TypeError(String::New("Wrong arguments")));
return scope.Close(Undefined());
}

Local<Number> num = Number::New(args[0]->NumberValue() + args[1]->NumberValue());//将参数转化成C++可用的double型相加,然后再转化成输出给node的Number型,最后赋值给 Local<Number> 类型的变量 num,返回给用户
return scope.Close(num);
}

void Init(Handle<Object> target) {
target->Set(String::NewSymbol("add"),
FunctionTemplate::New(Add)->GetFunction());
}

NODE_MODULE(addon, Init)

代码例子很简单,我们主要看到了几个新东西:

String::New("Wrong arguments")

这表示将C++字符串转化成js字符串对象

args[0]->NumberValue()

这表示将js传递过来的Number型变为C++可用的double型
具体在v8参考手册上有更多的类型变化,链接地址
V8的接口Local<xxx> 这种类型都是用来将C++的数据传输给js的
V8命名空间下的一些类,比如String,Date等都是用来将c++的数据转换为输出给js用的,当然这些类都继承自value类
感觉模块工作流程是这样的,接受node端的参数,如果要运算则变成c++类型的数据,进行运算,然后再将计算完成的数据转化成js的数据类型返回给js端。

上面c++模块js调用代码

var addon = require('./build/Release/addon');

console.log( 'This should be eight:', addon.add(3,5) );



我们继续看官网给出的下一个例子代码:
实现一个js回调函数,参数为"hello wrold"

#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]); //将参数第一个也就是js传递过来的回调函数转化成c++的function
const unsigned argc = 1; //定义无符号int argc为1
Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) };//创建一个数组,长度为1,
cb->Call(Context::GetCurrent()->Global(), argc, argv);//利用local<function>的call方法然后将上下文,参数长度,以及参数数组传入call方法,即调用js端的回调

return scope.Close(Undefined()); //返回undeined,无返回值
}

void Init(Handle<Object> target) {
target->Set(String::NewSymbol("runCallback"),
FunctionTemplate::New(RunCallback)->GetFunction());
}

NODE_MODULE(addon, Init)

js端调用代码:

var addon = require('./build/Release/addon');

addon.runCallback(function(msg){
console.log(msg); // 'hello world'
});


最后一个例子:
用c++创建一个js的对象,我就不多说了,会在最后一个例子体现,接下来我们简单做一个测试
有如下node代码:

console.time('for');

var ary = [];
for(var i=0;i<10000000;i++){
ary.push({"aaa":123})
}


console.timeEnd('for');

我们向一个数组push对象1千万个,输出时间为:

for: 3972ms

我们编写一个同样功能的c++代码:

#define BUILDING_NODE_EXTENSION
#include <node.h>

using namespace v8;

Handle<Value> CreateObject(const Arguments& args) {
HandleScope scope;

Handle<Value> k = args[0]->ToString(); //将传递的第一个参数转化为String类型
Local<Object> obj = Object::New();//创建一个js的空对象

if(args[1]->IsBoolean()){ //如果第二个参数是布尔值,则转化为布尔型
obj->Set(k, args[1]->ToBoolean());
}
else if(args[1]->IsNumber()){ //如果第二个参数是数值,则转化为整形
obj->Set(k, args[1]->ToNumber());
}
else obj->Set(k, args[1]->ToString()); //否则转化为字符串

int n = args[2]->NumberValue(); //将第三个参数转化为double


Handle<Array> ary = Array::New(n); //构建js数组,长度为n
for(int i=0;i<n;i++){
ary->Set(Integer::New(i), obj); //循环的往js数组内插入obj对象
}

return scope.Close(ary); //将数组输出给node端
}

void Init(Handle<Object> target) {
target->Set(String::NewSymbol("createObject"),
FunctionTemplate::New(CreateObject)->GetFunction());
}

NODE_MODULE(object, Init)

node端的代码:

console.time('for');

var cr= require('./addon/build/Release/object.node').createObject;
var ary = cr("aaa",123,10000000);

console.timeEnd('for');

执行时间:

for: 2114ms


个人感觉在处理一些大数据量的时候使用c++的模块可以大大的提升速度,比如去数据库获取大量数据进行筛选重组,然后传递给node,其他应用场景有待研究