环境:
win7
VS2010
node-gyp:1.0.1
python:2.7.3
nodejs:0.10.32
Node为了实现跨平台的编译,采用了Google的GYP(Generate Your Projects)来对项目进行管理。
安装node-gyp
npm isntall -g node-gyp
新建hello.cc:
Node的JavaScript引擎用的是Google开源的V8 JavaScript引擎(Chrome浏览器所用的引擎),所以简单介绍下v8中的一些概念:
Handle:一个handle就是指向一个对象的指针。v8中所有的对象都是使用handle来进行访问,之所以用它是因为v8的垃圾回收器需要。
HandleScope:可以把它想象成是多个Handle的一个容器。
#include
#include
using namespace v8;
Handle Method(const Arguments& args) {
HandleScope scope;
return scope.Close(String::New("world"));
}
void init(Handle
新建binding.gyp:
binding.gyp是默认的项目定义文件
{
"targets": [
{
"target_name": "hello",
"sources": [ "hello.cc" ]
}
]
}
之后我们以binding.gyp文件来生成Makefile等编译所需的文件:
node-gyp configure
E:\workspace\addon>node-gyp configure
gyp info it worked if it ends with ok
gyp info using [email protected]
gyp info using [email protected] | win32 | x64
gyp info spawn C:\Python27\python.exe
gyp info spawn args [ 'C:\\Users\\ucloud01\\AppData\\Roaming\\npm\\node_modules\
\node-gyp\\gyp\\gyp_main.py',
gyp info spawn args 'binding.gyp',
gyp info spawn args '-f',
gyp info spawn args 'msvs',
gyp info spawn args '-G',
gyp info spawn args 'msvs_version=auto',
gyp info spawn args '-I',
gyp info spawn args 'E:\\workspace\\addon\\build\\config.gypi',
gyp info spawn args '-I',
gyp info spawn args 'C:\\Users\\ucloud01\\AppData\\Roaming\\npm\\node_modules\
\node-gyp\\addon.gypi',
gyp info spawn args '-I',
gyp info spawn args 'C:\\Users\\ucloud01\\.node-gyp\\0.10.32\\common.gypi',
gyp info spawn args '-Dlibrary=shared_library',
gyp info spawn args '-Dvisibility=default',
gyp info spawn args '-Dnode_root_dir=C:\\Users\\ucloud01\\.node-gyp\\0.10.32',
gyp info spawn args '-Dmodule_root_dir=E:\\workspace\\addon',
gyp info spawn args '--depth=.',
gyp info spawn args '--no-parallel',
gyp info spawn args '--generator-output',
gyp info spawn args 'E:\\workspace\\addon\\build',
gyp info spawn args '-Goutput_dir=.' ]
gyp info ok
node-gyp build
E:\workspace\addon>node-gyp build
gyp info it worked if it ends with ok
gyp info using [email protected]
gyp info using [email protected] | win32 | x64
gyp info spawn C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe
gyp info spawn args [ 'build/binding.sln',
gyp info spawn args '/clp:Verbosity=minimal',
gyp info spawn args '/nologo',
gyp info spawn args '/p:Configuration=Release;Platform=x64' ]
在此解决方案中一次生成一个项目。若要启用并行生成,请添加“/m”开关。
hello.cc
C:\Users\ucloud01\.node-gyp\0.10.32\deps\v8\include\v8.h(184): warning C4506: 内
联函数“v8::Persistent v8::Persistent::New(v8::Handle)”没有定义 [E:\wo
rkspace\
addon\build\hello.vcxproj]
with
[
T=v8::Object
]
正在创建库 E:\workspace\addon\build\Release\hello.lib 和对象 E:\workspace\a
ddon\bu
ild\Release\hello.exp
正在生成代码
已完成代码的生成
hello.vcxproj -> E:\workspace\addon\build\Release\\hello.node
gyp info ok
测试:hello.js
var addon = require('./build/Release/hello');
console.log(addon.hello()); // 'world'
输出:
E:\workspace\addon>node hello
world
addon.cc:
#define BUILDING_NODE_EXTENSION
#include
using namespace v8;
Handle 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 num = Number::New(args[0]->NumberValue() +
args[1]->NumberValue());
return scope.Close(num);
}
void Init(Handle
binding.gyp:
{
"targets": [
{
"target_name": "addon",
"sources": [ "addon.cc" ]
}
]
}
node-gyp configure
node-gyp build
test:
addon_test.js
var addon = require('./src/build/Release/addon');
console.log( 'This should be 9:', addon.add(4,5) );
E:\workspace\addon>node addon_test.js
This should be 9: 9
传递一个javascript函数到C++模板处理
addon.cc
#define BUILDING_NODE_EXTENSION
#include
using namespace v8;
Handle RunCallback(const Arguments& args) {
HandleScope scope;
Local cb = Local::Cast(args[0]);
const unsigned argc = 1;
Local argv[argc] = { Local::New(String::New("hello world")) };
cb->Call(Context::GetCurrent()->Global(), argc, argv);
return scope.Close(Undefined());
}
void Init(Handle
test
addon_test.js:
var addon = require('./src/build/Release/addon');
addon(function(msg){
console.log(msg); // 'hello world'
});
E:\workspace\addon>node addon_test.js
hello world
#define BUILDING_NODE_EXTENSION
#include
using namespace v8;
Handle CreateObject(const Arguments& args) {
HandleScope scope;
Local obj = Object::New();
obj->Set(String::NewSymbol("msg"), args[0]->ToString());
return scope.Close(obj);
}
void Init(Handle exports, Handle module) {
module->Set(String::NewSymbol("exports"),
FunctionTemplate::New(CreateObject)->GetFunction());
}
NODE_MODULE(addon, Init)
addon_test.js:
var addon = require('./src/build/Release/addon');
var obj1 = addon('hello');
var obj2 = addon('world');
console.log(obj1.msg+' '+obj2.msg); // 'hello world'
result:
E:\workspace\addon>node addon_test.js
hello world
addon.cc
#define BUILDING_NODE_EXTENSION
#include
using namespace v8;
Handle MyFunction(const Arguments& args) {
HandleScope scope;
return scope.Close(String::New("hello world"));
}
Handle CreateFunction(const Arguments& args) {
HandleScope scope;
Local tpl = FunctionTemplate::New(MyFunction);
Local fn = tpl->GetFunction();
fn->SetName(String::NewSymbol("theFunction")); // omit this to make it anonymous
return scope.Close(fn);
}
void Init(Handle exports, Handle module) {
module->Set(String::NewSymbol("exports"),
FunctionTemplate::New(CreateFunction)->GetFunction());
}
NODE_MODULE(addon, Init)
addon_test.js
var addon = require('./src/build/Release/addon');
var fn = addon();
console.log(fn()); // 'hello world'
result:
E:\workspace\addon>node addon_test.js
hello world
c++主文件 addon.cc
#define BUILDING_NODE_EXTENSION
#include
#include "myobject.h"
using namespace v8;
void InitAll(Handle exports) {
MyObject::Init(exports);
}
NODE_MODULE(addon, InitAll)
在myobject.h中继承node::ObjectWrap
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include
class MyObject : public node::ObjectWrap {
public:
static void Init(v8::Handle exports);
private:
explicit MyObject(double value = 0);
~MyObject();
static v8::Handle New(const v8::Arguments& args);
static v8::Handle PlusOne(const v8::Arguments& args);
static v8::Persistent constructor;
double value_;
};
#endif
myobject.cc实现函数
#define BUILDING_NODE_EXTENSION
#include
#include "myobject.h"
using namespace v8;
Persistent MyObject::constructor;
MyObject::MyObject(double value) : value_(value) {
}
MyObject::~MyObject() {
}
void MyObject::Init(Handle exports) {
// Prepare constructor template
Local tpl = FunctionTemplate::New(New);
tpl->SetClassName(String::NewSymbol("MyObject"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
// Prototype
tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),
FunctionTemplate::New(PlusOne)->GetFunction());
constructor = Persistent::New(tpl->GetFunction());
exports->Set(String::NewSymbol("MyObject"), constructor);
}
Handle 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 argv[argc] = { args[0] };
return scope.Close(constructor->NewInstance(argc, argv));
}
}
Handle MyObject::PlusOne(const Arguments& args) {
HandleScope scope;
MyObject* obj = ObjectWrap::Unwrap(args.This());
obj->value_ += 1;
return scope.Close(Number::New(obj->value_));
}
test:
var addon = require('./src/build/Release/addon');
var obj = new addon.MyObject(10);
console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13
记得修改binding.gyp:
{
"targets": [
{
"target_name": "addon",
"sources": [ "addon.cc" , "myobject.cc"]
}
]
}
result:
E:\workspace\addon>node addon_test.js
11
12
13
#define BUILDING_NODE_EXTENSION
#include
#include "myobject.h"
using namespace v8;
Handle CreateObject(const Arguments& args) {
HandleScope scope;
return scope.Close(MyObject::NewInstance(args));
}
void InitAll(Handle exports, Handle module) {
MyObject::Init();
module->Set(String::NewSymbol("exports"),
FunctionTemplate::New(CreateObject)->GetFunction());
}
NODE_MODULE(addon, InitAll)
myobject.h
#define BUILDING_NODE_EXTENSION
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include
class MyObject : public node::ObjectWrap {
public:
static void Init();
static v8::Handle NewInstance(const v8::Arguments& args);
private:
explicit MyObject(double value = 0);
~MyObject();
static v8::Handle New(const v8::Arguments& args);
static v8::Handle PlusOne(const v8::Arguments& args);
static v8::Persistent constructor;
double value_;
};
#endif
myobject.cc
#define BUILDING_NODE_EXTENSION
#include
#include "myobject.h"
using namespace v8;
Persistent MyObject::constructor;
MyObject::MyObject(double value) : value_(value) {
}
MyObject::~MyObject() {
}
void MyObject::Init() {
// Prepare constructor template
Local tpl = FunctionTemplate::New(New);
tpl->SetClassName(String::NewSymbol("MyObject"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
// Prototype
tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),
FunctionTemplate::New(PlusOne)->GetFunction());
constructor = Persistent::New(tpl->GetFunction());
}
Handle 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 argv[argc] = { args[0] };
return scope.Close(constructor->NewInstance(argc, argv));
}
}
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);
}
Handle MyObject::PlusOne(const Arguments& args) {
HandleScope scope;
MyObject* obj = ObjectWrap::Unwrap(args.This());
obj->value_ += 1;
return scope.Close(Number::New(obj->value_));
}
test:
var createObject = require('./src/build/Release/addon');
var obj = createObject(10);
console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13
var obj2 = createObject(20);
console.log( obj2.plusOne() ); // 21
console.log( obj2.plusOne() ); // 22
console.log( obj2.plusOne() ); // 23
result:
E:\workspace\addon>node addon_test.js
11
12
13
21
22
23
传递封装的对象
#define BUILDING_NODE_EXTENSION
#include
#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());
MyObject* obj2 = node::ObjectWrap::Unwrap(
args[1]->ToObject());
double sum = obj1->getValue() + obj2->getValue();
return scope.Close(Number::New(sum));
}
void InitAll(Handle 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
#define BUILDING_NODE_EXTENSION
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include
class MyObject : public node::ObjectWrap {
public:
static void Init();
static v8::Handle NewInstance(const v8::Arguments& args);
double getValue() const {return value_;}
private:
explicit MyObject(double value = 0);
~MyObject();
static v8::Handle New(const v8::Arguments& args);
static v8::Handle PlusOne(const v8::Arguments& args);
static v8::Persistent constructor;
double value_;
};
#endif
myobject,cc
#define BUILDING_NODE_EXTENSION
#include
#include "myobject.h"
using namespace v8;
Persistent MyObject::constructor;
MyObject::MyObject(double value) : value_(value) {
}
MyObject::~MyObject() {
}
void MyObject::Init() {
// Prepare constructor template
Local tpl = FunctionTemplate::New(New);
tpl->SetClassName(String::NewSymbol("MyObject"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
// Prototype
tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),
FunctionTemplate::New(PlusOne)->GetFunction());
constructor = Persistent::New(tpl->GetFunction());
}
Handle 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 argv[argc] = { args[0] };
return scope.Close(constructor->NewInstance(argc, argv));
}
}
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);
}
Handle MyObject::PlusOne(const Arguments& args) {
HandleScope scope;
MyObject* obj = ObjectWrap::Unwrap(args.This());
obj->value_ += 1;
return scope.Close(Number::New(obj->value_));
}
var addon = require('./src/build/Release/addon');
var obj1 = addon.createObject(10);
var obj2 = addon.createObject(20);
var result = addon.add(obj1, obj2);
console.log(result); // 30
result:
E:\workspace\addon>node addon_test.js
30
这里把文档中的
double Value() const {return value_;}
double getValue() const {return value_;}
不然会报错