nodejs添加C++模块

来源:互联网 发布:编程c图形库砖块金字塔 编辑:程序博客网 时间:2024/06/05 02:54

环境:

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 <node.h>#include <v8.h>using namespace v8;Handle<Value> Method(const Arguments& args) {  HandleScope scope;  return scope.Close(String::New("world"));}void init(Handle<Object> 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 configuregyp info it worked if it ends with okgyp info using node-gyp@1.0.2gyp info using node@0.10.32 | win32 | x64gyp info spawn C:\Python27\python.exegyp 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 buildgyp info it worked if it ends with okgyp info using node-gyp@1.0.2gyp info using node@0.10.32 | win32 | x64gyp info spawn C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exegyp 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.ccC:\Users\ucloud01\.node-gyp\0.10.32\deps\v8\include\v8.h(184): warning C4506: 内联函数“v8::Persistent<T> v8::Persistent<T>::New(v8::Handle<T>)”没有定义 [E:\workspace\addon\build\hello.vcxproj]          with          [              T=v8::Object          ]     正在创建库 E:\workspace\addon\build\Release\hello.lib 和对象 E:\workspace\addon\bu  ild\Release\hello.exp  正在生成代码  已完成代码的生成  hello.vcxproj -> E:\workspace\addon\build\Release\\hello.nodegyp info ok

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

测试:hello.js

var addon = require('./build/Release/hello');console.log(addon.hello()); // 'world'
输出:

E:\workspace\addon>node helloworld

向C++模块传递参数

addon.cc:

#define BUILDING_NODE_EXTENSION#include <node.h>using namespace v8;Handle<Value> 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<Number> num = Number::New(args[0]->NumberValue() +      args[1]->NumberValue());  return scope.Close(num);}void Init(Handle<Object> 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.jsThis should be 9: 9

回调

传递一个javascript函数到C++模板处理

addon.cc

#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]);  const unsigned argc = 1;  Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) };  cb->Call(Context::GetCurrent()->Global(), argc, argv);  return scope.Close(Undefined());}void Init(Handle<Object> exports, Handle<Object> 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.jshello world

用C++模块创建object代理

addon.cc:

#define BUILDING_NODE_EXTENSION#include <node.h>using namespace v8;Handle<Value> CreateObject(const Arguments& args) {  HandleScope scope;  Local<Object> obj = Object::New();  obj->Set(String::NewSymbol("msg"), args[0]->ToString());  return scope.Close(obj);}void Init(Handle<Object> exports, Handle<Object> 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.jshello world

函数代理

addon.cc

#define BUILDING_NODE_EXTENSION#include <node.h>using namespace v8;Handle<Value> MyFunction(const Arguments& args) {  HandleScope scope;  return scope.Close(String::New("hello world"));}Handle<Value> CreateFunction(const Arguments& args) {  HandleScope scope;  Local<FunctionTemplate> tpl = FunctionTemplate::New(MyFunction);  Local<Function> fn = tpl->GetFunction();  fn->SetName(String::NewSymbol("theFunction")); // omit this to make it anonymous  return scope.Close(fn);}void Init(Handle<Object> exports, Handle<Object> 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.jshello world

封装C++对象

c++主文件  addon.cc

#define BUILDING_NODE_EXTENSION#include <node.h>#include "myobject.h"using namespace v8;void InitAll(Handle<Object> exports) {  MyObject::Init(exports);}NODE_MODULE(addon, InitAll)
在myobject.h中继承node::ObjectWrap

#ifndef MYOBJECT_H#define MYOBJECT_H#include <node.h>class MyObject : public node::ObjectWrap { public:  static void Init(v8::Handle<v8::Object> exports); private:  explicit MyObject(double value = 0);  ~MyObject();  static v8::Handle<v8::Value> New(const v8::Arguments& args);  static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args);  static v8::Persistent<v8::Function> constructor;  double value_;};#endif
myobject.cc实现函数

#define BUILDING_NODE_EXTENSION#include <node.h>#include "myobject.h"using namespace v8;Persistent<Function> MyObject::constructor;MyObject::MyObject(double value) : value_(value) {}MyObject::~MyObject() {}void MyObject::Init(Handle<Object> exports) {  // Prepare constructor template  Local<FunctionTemplate> 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<Function>::New(tpl->GetFunction());  exports->Set(String::NewSymbol("MyObject"), constructor);}Handle<Value> 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<Value> argv[argc] = { args[0] };    return scope.Close(constructor->NewInstance(argc, argv));  }}Handle<Value> MyObject::PlusOne(const Arguments& args) {  HandleScope scope;  MyObject* obj = ObjectWrap::Unwrap<MyObject>(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() ); // 11console.log( obj.plusOne() ); // 12console.log( obj.plusOne() ); // 13
记得修改binding.gyp:

{  "targets": [    {      "target_name": "addon",      "sources": [ "addon.cc" , "myobject.cc"]    }  ]}
result:

E:\workspace\addon>node addon_test.js111213

封装对象的代理

addon.cc
#define BUILDING_NODE_EXTENSION#include <node.h>#include "myobject.h"using namespace v8;Handle<Value> CreateObject(const Arguments& args) {  HandleScope scope;  return scope.Close(MyObject::NewInstance(args));}void InitAll(Handle<Object> exports, Handle<Object> 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 <node.h>class MyObject : public node::ObjectWrap { public:  static void Init();  static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args); private:  explicit MyObject(double value = 0);  ~MyObject();  static v8::Handle<v8::Value> New(const v8::Arguments& args);  static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args);  static v8::Persistent<v8::Function> constructor;  double value_;};#endif
myobject.cc
#define BUILDING_NODE_EXTENSION#include <node.h>#include "myobject.h"using namespace v8;Persistent<Function> MyObject::constructor;MyObject::MyObject(double value) : value_(value) {}MyObject::~MyObject() {}void MyObject::Init() {  // Prepare constructor template  Local<FunctionTemplate> 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<Function>::New(tpl->GetFunction());}Handle<Value> 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<Value> argv[argc] = { args[0] };    return scope.Close(constructor->NewInstance(argc, argv));  }}Handle<Value> MyObject::NewInstance(const Arguments& args) {  HandleScope scope;  const unsigned argc = 1;  Handle<Value> argv[argc] = { args[0] };  Local<Object> instance = constructor->NewInstance(argc, argv);  return scope.Close(instance);}Handle<Value> MyObject::PlusOne(const Arguments& args) {  HandleScope scope;  MyObject* obj = ObjectWrap::Unwrap<MyObject>(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() ); // 11console.log( obj.plusOne() ); // 12console.log( obj.plusOne() ); // 13var obj2 = createObject(20);console.log( obj2.plusOne() ); // 21console.log( obj2.plusOne() ); // 22console.log( obj2.plusOne() ); // 23
result:
E:\workspace\addon>node addon_test.js111213212223
传递封装的对象
addon.cc
#define BUILDING_NODE_EXTENSION#include <node.h>#include "myobject.h"using namespace v8;Handle<Value> CreateObject(const Arguments& args) {  HandleScope scope;  return scope.Close(MyObject::NewInstance(args));}Handle<Value> Add(const Arguments& args) {  HandleScope scope;  MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>(      args[0]->ToObject());  MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>(      args[1]->ToObject());  double sum = obj1->getValue() + obj2->getValue();  return scope.Close(Number::New(sum));}void InitAll(Handle<Object> 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 <node.h>class MyObject : public node::ObjectWrap { public:  static void Init();  static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args);  double getValue() const {return value_;} private:  explicit MyObject(double value = 0);  ~MyObject();  static v8::Handle<v8::Value> New(const v8::Arguments& args);  static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args);  static v8::Persistent<v8::Function> constructor;  double value_;};#endif
myobject,cc
<pre name="code" class="cpp">
#define BUILDING_NODE_EXTENSION#include <node.h>#include "myobject.h"using namespace v8;Persistent<Function> MyObject::constructor;MyObject::MyObject(double value) : value_(value) {}MyObject::~MyObject() {}void MyObject::Init() {  // Prepare constructor template  Local<FunctionTemplate> 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<Function>::New(tpl->GetFunction());}Handle<Value> 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<Value> argv[argc] = { args[0] };    return scope.Close(constructor->NewInstance(argc, argv));  }}Handle<Value> MyObject::NewInstance(const Arguments& args) {  HandleScope scope;  const unsigned argc = 1;  Handle<Value> argv[argc] = { args[0] };  Local<Object> instance = constructor->NewInstance(argc, argv);  return scope.Close(instance);}Handle<Value> MyObject::PlusOne(const Arguments& args) {  HandleScope scope;  MyObject* obj = ObjectWrap::Unwrap<MyObject>(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.js30
这里把文档中的
double Value() const {return value_;}
改成了
double getValue() const {return value_;}
不然会报错

1 0
原创粉丝点击