nodejs添加C++模块

环境:

win7

VS2010

node-gyp:1.0.1

python:2.7.3

nodejs:0.10.32


HELLO

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 exports) {
  exports->Set(String::NewSymbol("hello"),
      FunctionTemplate::New(Method)->GetFunction());
}

NODE_MODULE(hello, init) 
  

新建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

执行完成之后,在项目目录下会生成一个build目录,里边是gyp自动生成的一些编译所需文件。这一步完成之后,我们来进行编译操作:

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

如果执行没有问题的话,在build/Release目录下会生成hello.node文件,即我们创建的hello模块

测试:hello.js

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

console.log(addon.hello()); // 'world'
输出:

E:\workspace\addon>node hello
world

向C++模块传递参数

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 exports) {
  exports->Set(String::NewSymbol("add"),
      FunctionTemplate::New(Add)->GetFunction());
}

NODE_MODULE(addon, Init)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) );

result:

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 exports, Handle module) {
  module->Set(String::NewSymbol("exports"),
      FunctionTemplate::New(RunCallback)->GetFunction());
}

NODE_MODULE(addon, Init)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

用C++模块创建object代理

addon.cc:

#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++对象

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

封装对象的代理

addon.cc
#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
传递封装的对象
addon.cc
#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_));
} 
   
 test: 
   
  
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_;}
不然会报错

你可能感兴趣的:(Node.js,杂货)