C++写node笔记(三)

1、return函数

执行函数返回hello world:

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
#define BUILDING_NODE_EXTENSION
#include <node.h>

using namespace v8;//命名空间

Handle<Value> MyFunction(const Arguments& args) {//定义一个C++函数,返回hello world
HandleScope scope;
return scope.Close(String::New("hello world"));
}

Handle<Value> CreateFunction(const Arguments& args) { //定义一个方法,用来return函数myfunction
HandleScope scope;

Local<FunctionTemplate> tpl = FunctionTemplate::New(MyFunction);//创建一个函数模版对象
Local<Function> fn = tpl->GetFunction(); //得到local对象,就是js需要的函数对象
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)

我们着重看下这2行代码:
Local tpl = FunctionTemplate::New(MyFunction);
Local fn = tpl->GetFunction();

fn->SetName(String::NewSymbol(“theFunction”));

FunctionTemplate是用来创建函数的,在一个上下文中仅能创建一个FunctionTemplate,创建的函数的声明周期等于这个上下文存在的声明周期,一个FunctionTemplate可以拥有属性,当你创建它后,这些属性可以加给他。

一个FunctionTemplate可以由原形模版,这个原形模版是用来创建原形链对象的。

下面是V8官网的代码示例:

 v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); //创建一个函数模版
t->Set("func_property", v8::Number::New(1)); //设置这个函数模版的属性 func_property 为number型1

v8::Local<v8::Template> proto_t = t->PrototypeTemplate(); //定义proto_t指向t的原形链的函数模版,也就是function类,每个函数对象都是function类的实例
proto_t->Set("proto_method", v8::FunctionTemplate::New(InvokeCallback)); //在t的原形链上定义proto_method方法,等于调用InvokeCallback
proto_t->Set("proto_const", v8::Number::New(2));//在function的原形链上增加静态属性2

v8::Local<v8::ObjectTemplate> instance_t = t->InstanceTemplate(); //获得t的实例模版
instance_t->SetAccessor("instance_accessor", InstanceAccessorCallback); //设置访问器
instance_t->SetNamedPropertyHandler(PropertyHandlerCallback, ...);
instance_t->Set("instance_property", Number::New(3));

v8::Local<v8::Function> function = t->GetFunction();//将t转化为js的function对象
v8::Local<v8::Object> instance = function->NewInstance();//实例化t,赋值给instance

下面是js对应的代码:

func_property in function == true;
function.func_property == 1;

function.prototype.proto_method() invokes ‘InvokeCallback’
function.prototype.proto_const == 2;

instance instanceof function == true;
instance.instance_accessor calls ‘InstanceAccessorCallback’
instance.instance_property == 3;
具体我们如何利用c++函数给node用我们了解了,大致的思路是:
先写C++函数,然后利用函数模版转化成js模版函数,接着为这个函数模版做一些属性设置,比如设置原形链或者静态属性等,最后调用这个函数模版指针的GetFunction方法,转化成local类型,供node使用。

2、包裹c++对象
官网接下来的例子是用C++的类Myobject输出给js,然后js通过new操作来获取它。我们先看代码:

#define BUILDING_NODE_EXTENSION

#include <node.h>

#include “myobject.h” //加载myobject文件

using namespace v8;

void InitAll(Handle target) {
MyObject::Init(target);//将target转递给Myobject类下的静态方法Init
}

NODE_MODULE(addon, InitAll)
我们来看myobject.h的代码:

#ifndef MYOBJECT_H //如果没有定义 MYOBJECT_H

#define MYOBJECT_H

#include <node.h>

class MyObject : public node::ObjectWrap {//声明类Myobject,public继承自node命名空间的ObjectWrap基类
public:
static void Init(v8::Handlev8::Object target); //声明静态方法Init

private:
MyObject(); //声明构造函数
~MyObject(); //声明析构函数

static v8::Handlev8::Value New(const v8::Arguments& args);//声明New方法
static v8::Handlev8::Value PlusOne(const v8::Arguments& args);//声明PlusOne方法
double counter_;
};

#endif
以上是类的定义,我们创建myobject.cc文件

#define BUILDING_NODE_EXTENSION

#include <node.h>

#include “myobject.h”

using namespace v8;

MyObject::MyObject() {};
MyObject::~MyObject() {};

void MyObject::Init(Handle target) {
// Prepare constructor template
Local tpl = FunctionTemplate::New(New);//定义一个函数模版tpl
tpl->SetClassName(String::NewSymbol(“MyObject”)); //设置函数模版的类名 MyObject
tpl->InstanceTemplate()->SetInternalFieldCount(1);//设置这个对象模版内部字段的数量
// Prototype
tpl->PrototypeTemplate()->Set(String::NewSymbol(“plusOne”),
FunctionTemplate::New(PlusOne)->GetFunction());//设置tpl的原形链上的方法,名叫plusOne

Persistent constructor = Persistent::New(tpl->GetFunction()); //获得构造函数
//Persistent类表示需要明确的调用Persistent::Dispose才回去GC
target->Set(String::NewSymbol(“MyObject”), constructor); //设置Myobject为构造函数
}

Handle MyObject::New(const Arguments& args) { //对外的new函数
HandleScope scope;

MyObject* obj = new MyObject();//实例化一个Myobject对象,用obj指针指向
obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(); //私有属性counter_初始化
obj->Wrap(args.This());//包裹 args对象

return args.This();
}

Handle MyObject::PlusOne(const Arguments& args) {
HandleScope scope;

MyObject* obj = ObjectWrap::Unwrap(args.This());//指针变量指向arg对象
obj->counter_ += 1; //obj对象的属性counter_加上了1

return scope.Close(Number::New(obj->counter_));//函数返回obj的counter属性
}
这里有几个需要我们仔细消化下:
tpl->InstanceTemplate()->SetInternalFieldCount(1);//设置从这个模版实例化的数量

InstanceTemplate方法用来获得tpl模版的实例的模版,返回Local而SetInternalFieldCount方法用来对这个对象模版设置内部字段的数量,这里设置为1.这里我也不是很明白,起初以为是只能设置对象的一个属性,后来发现不是那样的,谷歌查询之后发现是映射关系的参数。
Persistent constructor = Persistent::New(tpl->GetFunction()); //获得构造函数
//Persistent类表示需要明确的调用Persistent::Dispose才回去GC
target->Set(String::NewSymbol(“MyObject”), constructor); //设置Myobject为构造函数
Persistent这个类型不同于local,他不会自动的去GC,而要求用户手动去Dispose,所以这里就等于声明了全局变量,但是在node端这里用Local替换Persistent是一样的。
Javascript中的this指针可以通过Arguments::This ()得到
js代码:
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

3、官网接下来一个例子是用createObject方法来代替new关键字,和上面代码区别只有一块:
Handle MyObject::NewInstance(const Arguments& args) {
HandleScope scope;

const unsigned argc = 1;
Handle argv[argc] = { args[0] };
Local instance = constructor->NewInstance(argc, argv);

return scope.Close(instance);
}
官方api对NewInstance的定义:
V8EXPORT Local< Object > NewInstance (int argc, Handle< Value > argv[]) const
注意这里的NewInstance不是MyObject的方法。
js代码:
var addon = require(‘./build/Release/addon’);

var obj = addon.createObject(10);
console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13

var obj2 = addon.createObject(20);
console.log( obj2.plusOne() ); // 21
console.log( obj2.plusOne() ); // 22
console.log( obj2.plusOne() ); // 23
总结一下这2段程序的流程吧:
addon.cc文件
1、定义Init函数,用来初始化,在Init函数中执行Myobject的静态方法Init、
2、Init函数第二行定义一个对外的方法createobject
3、定义createobject方法,执行Myobject类的静态方法NewInstance,然后将其值返回。
myobject.h
1、MYOBJECT_H这个常量如果定义过则不重复定义,如果没有则执行myobject.h,防止重复加载
2、定义Myobject类,继承自node命名空间下的ObjectWrap基类
3、声明2个public的静态方法init和NewInstance
4、声明private的属性和方法,声明构造和析构函数,静态属性 constructor,静态方法new和plusone
最重要的myobject.cc文件,主要是对myobject类各种方法的定义(声明在类中已经做过)
1、将myobject.h包涵进来
2、定义构造和析构函数
3、定义Init函数
3-1、定义一个函数模版,模版来源是c++的myobject类的静态方法new
3-2、设置模版函数tpl的class名,为Myobject
3-3、设置这个class名为myobject的构造函数(js中叫这个吧,不是基类)的原形链上的方法plusone,并且把c++中myobject类的静态私有方法plusone作为函数模版赋值给它
3-4、最后将constructor变量指向tpl转化为的Persistent类型的js函数对象。
4、定义new函数
4-1、创建一个指向Myobject类的实例的指针变量 obj
4-2、将这个实例的counter_属性初始赋值为0或者参数
4-3、将local对象wrap包装
4-4、返回local,其实这里就是js实例的对象,就是js的createobject()方法的返回值
5、定义NewInstance方法
5-1、生成参数,然后调用
Local instance = constructor->NewInstance(argc, argv);
上面这段其实就是生成tpl函数模版的实例,同时将参数传进去,就相当于执行了Myobject::new的方法
5-2、最后将生成的实例通过scope.Close返回给客户端
6、定义PlusOne方法,将js的实例对象转化为C++对应的实例对象指针,然后执行属性counter_加1,最后返回数据

4、wrap和unwrap
除了wrap node 对象C++对象以外,我们还可以用过node::ObjectWrap::Unwrap这个方法来unwrap的node对象,供C++使用
wrap.cc代码:

#define BUILDING_NODE_EXTENSION

#include <node.h>

#include “myobject.h”

using namespace v8;

Handle CreateObject(const Arguments& args) {
HandleScope scope;
return scope.Close(MyObject::NewInstance(args));
}

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

MyObject obj1 = node::ObjectWrap::Unwrap(
args[0]->ToObject());//这里告诉我们如何利用Unwrap将node的对象转化为C++的对象
MyObject
obj2 = node::ObjectWrap::Unwrap(
args[1]->ToObject());

double sum = obj1->Val() + obj2->Val();
return scope.Close(Number::New(sum));
}

void InitAll(Handle target) {
MyObject::Init();

target->Set(String::NewSymbol(“createObject”),
FunctionTemplate::New(CreateObject)->GetFunction());

target->Set(String::NewSymbol(“add”),
FunctionTemplate::New(Add)->GetFunction());
}

NODE_MODULE(addon, InitAll)

上面这段代码重点就是:
MyObject* obj1 = node::ObjectWrap::Unwrap(
args[0]->ToObject());//这里告诉我们如何利用Unwrap将node的对象转化为C++的对象

我们可以利用这段代码,将node端的对象转化C++端的对象。

mywrap.h

#define BUILDING_NODE_EXTENSION

#ifndef MYOBJECT_H

#define MYOBJECT_H

#include <node.h>

class MyObject : public node::ObjectWrap {
public:
static void Init();
static v8::Handlev8::Value NewInstance(const v8::Arguments& args);
double Val() const { return val_; }

private:
MyObject();
~MyObject();

static v8::Persistentv8::Function constructor;
static v8::Handlev8::Value New(const v8::Arguments& args);
double val_;
};

#endif
mywrap.cc

#define BUILDING_NODE_EXTENSION

#include <node.h>

#include “myobject.h”

using namespace v8;

MyObject::MyObject() {};
MyObject::~MyObject() {};

Persistent MyObject::constructor;

void MyObject::Init() {
// Prepare constructor template
Local tpl = FunctionTemplate::New(New);
tpl->SetClassName(String::NewSymbol(“MyObject”));
tpl->InstanceTemplate()->SetInternalFieldCount(1);

constructor = Persistent::New(tpl->GetFunction());
}

Handle MyObject::New(const Arguments& args) {
HandleScope scope;

MyObject* obj = new MyObject();
obj->val_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
obj->Wrap(args.This());

return args.This();
}

Handle MyObject::NewInstance(const Arguments& args) {
HandleScope scope;

const unsigned argc = 1;
Handle argv[argc] = { args[0] };
Local instance = constructor->NewInstance(argc, argv);

return scope.Close(instance);
}
js代码:
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
这段代码和上面大同小异~