node.js调用C++说明文档

对官方NAN模块的说明进行翻译。

1、用法

只需要简单的在package.json中加入如下语句:

$ npm install ---save nan

在bind.gyp中加入NAN路径,这样才能够在.cpp文件中使用命名空间#include

 

“include_dirs”:[“

2、例子

将官方版本的例子下载下来,https://github.com/nodejs/node-addon-examples,运行其中的例子,发现一些问题。

(1)helloworld里面一共三种c++引入node的方式(NAN,node0.10,node0.12),其中nan文件夹包含的hello.cc文件引用了,在binding.gyp中配置了文件路径,包括两个函数,Method函数与Init函数,经过编译测试可以用(其中需要npm下载包bindings);nan0.1会报错,莫名其妙的错误(

make: *** [Release/obj.target/hello/hello.o]Error 1

make: Leaving directory`/home/neil/Projects/Nodejs/cps/src/example-nan/1_hello_world/node_0.10/build'

gyp ERR! build error

gyp ERR! stack Error: `make` failed withexit code: 2

gyp ERR! stack     at ChildProcess.onExit(/usr/local/lib/node_modules/node-gyp/lib/build.js:276:23)

gyp ERR! stack     at emitTwo (events.js:87:13)

gyp ERR! stack     at ChildProcess.emit (events.js:172:7)):思考了许久,查阅资料,牛人说大概是因为V8引擎经过了一次大的升级,里面的c++函数库并不能兼容导致的错误Handle Method这种写法已经不行了,(坑爹啊,这V8跨度如此大,不怕扯着档了么)。

然后继续对node0.12版本进行编译,发现也能通过。仔细对比这三种方式,得出它们的区别如下:

首先,第一种使用NAN包方式实现对c函数的调用,核心部分需要写两个函数,

第一个是Method函数,定义回调方法,第二个是初始化函数Init,将Method函数中的world在js中输出。这个hello world的基本的功能就结束了。

 

(2)第二个例子中的function arguments同样也分为三种调用方式,这里只使第一个(NAN的调用方式)。与第一部分helloworld不同,这部分给出了如何在c与js之间进行参数定义与传递。具体方法如下:

首先定义一个.cc文件,里面用c进行定义函数,定义空函数。

void Add(const Nan::FunctionCallbackInfo&info) {//定义函数

 
if (info.Length()< 2) {//判断传入参数是否满足要求
    Nan::ThrowTypeError(
"Wrong number of arguments");
   
return;
  }

 
if (!info[0]->IsNumber() || !info[1]->IsNumber()) {
    Nan::ThrowTypeError(
"Wrong arguments");
   
return;
  }

 
double arg0= info[0]->NumberValue();//取参数
 
double arg1= info[1]->NumberValue();
  v8::Local num =Nan::New(arg0 + arg1);//
计算(注意该处参数类型的写法)

  info.GetReturnValue().Set(num);//
返回计算结果
}

将该add方法导出:


void Init(v8::Local exports) {//初始化函数导出add方法
  exports->Set(Nan::New("add").ToLocalChecked(),//引号中为导出以后的函数名,在js中调用
               Nan::New(Add)->GetFunction());//括号中为C中定义的函数
}

NODE_MODULE(addon, Init)

 

在binding.gyp里面写:

{
  "targets": [
    {
      "target_name": "addon",
      "sources": [ "addon.cc" ],
      "include_dirs": [
        "

通过node-gyp configure build命令可以进行编译。

然后在addon.js中队这个add函数进行调用

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

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

得到结果。

可见上面几个文件中,最为关键的是对add函数的定义方式,这里传入参数的写法很特殊,必须以const Nan::FunctionCallbackInfo& info的形式,返回值也必须是V8的内部格式,经过NAN::new的处理赋值,不然会报错。最后返回不是用return而是利用info.GetReturnValue().Set(num)来完成。

 

(3)第三个例子是关于回调函数的callback的,进入nan目录,其他方面都和上面两个例子一样,唯一不同的是在addon.cc文件中对回调函数的定义。

#include 

void RunCallback(const Nan::FunctionCallbackInfo& info) {//定义方式一样
  v8::Local cb = info[0].As();//定义第一个参数为回调函数
  const unsigned argc = 1;
  v8::Local argv[argc] = { Nan::New("hello world").ToLocalChecked() };//定义字符串指针argv
  Nan::MakeCallback(Nan::GetCurrentContext()->Global(), cb, argc, argv);将cb函数定义为一个字符串指针,并映射到全局,MakeCallback有四个参数,其中第一个是目标,Global()代表映射到全局,二个参数是函数名称,第三个参数为argc,第四个参数为字符串指针或者其他操作。
}

void Init(v8::Local exports, v8::Local module) {//初始化函数,将回调函数进行映射
  Nan::SetMethod(module, "exports", RunCallback);//通过SetMethod函数将RunCallback函数映射到js中的module去,由于此处为回调函数,所以必须用SetMethod而不是第二例子方法使用的V8objects的Set函数。
}

NODE_MODULE(addon, Init)

然后编译,在js文件中对回调函数的调用方式如下:

var addon = require('bindings')('addon');

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

(4)第4个例子是关于对象工厂的实现,所为对象工厂就是通过c函数构建的对象传递给nodejs调用。在addon.cc文件中有如下代码

#include 

void CreateObject(const Nan::FunctionCallbackInfo& info) {
  v8::Local obj = Nan::New();//定义对象
  obj->Set(Nan::New("msg").ToLocalChecked(), info[0]->ToString());//将info给对象属性msg赋值

  info.GetReturnValue().Set(obj);//返回该对象
}

void Init(v8::Local exports, v8::Local module) {
  module->Set(Nan::New("exports").ToLocalChecked(),
      Nan::New(CreateObject)->GetFunction());//将创建对象函数CreateObject映射到js中
}

NODE_MODULE(addon, Init)

在js端对创建对象函数的调用代码如下:

var addon = require('bindings')('addon');//需要安装bindingsvar obj1 = addon('hello');//将‘hello’参数传递进去,生产obj1对象
var obj2 = addon('world');
console.log(obj1.msg+' '+obj2.msg); // 'hello world'

(5)第5个例子是关于函数工厂的实现,与对象工厂几乎类似,只是将对象变成了函数。

(6)第6个例子是关于对象封装。主要是实现在c语言中对象的传递使用,首先进入头文件myobjects.h:

#ifndef MYOBJECT_H
#define MYOBJECT_H

#include 

class MyObject : public Nan::ObjectWrap {//基本的写法,继承
 public:
  static void Init(v8::Local exports);//必须定义初始化函数

 private:
  explicit MyObject(double value = 0);//构造函数
  ~MyObject();
//成员函数,必须定义成静态的
  static void New(const Nan::FunctionCallbackInfo& info); 
  static void GetValue(const Nan::FunctionCallbackInfo& info);
  static void PlusOne(const Nan::FunctionCallbackInfo& info);
  static void Multiply(const Nan::FunctionCallbackInfo& info);
  static Nan::Persistent constructor;
  double value_;
};

#endif

然后进如myobjects.cpp文件

#include "myobject.h"

Nan::Persistent MyObject::constructor;

MyObject::MyObject(double value) : value_(value) {
}

MyObject::~MyObject() {
}

void MyObject::Init(v8::Local exports) {//初始化函数
  Nan::HandleScope scope;//定义scope范围

  // Prepare constructor template
  v8::Local tpl = Nan::New(New);//定义V8函数模板
  tpl->SetClassName(Nan::New("MyObject").ToLocalChecked());//定义名称
  tpl->InstanceTemplate()->SetInternalFieldCount(1);//将函数模板放入到定义范围中

  // Prototype将函数映射到js中,Nan::SetPrototypeMethod第一个参数是函数模板,第二个参数为js调用函数名称,第三个参数为c语言中的函数名称
  Nan::SetPrototypeMethod(tpl, "value", GetValue);
  Nan::SetPrototypeMethod(tpl, "plusOne", PlusOne);
  Nan::SetPrototypeMethod(tpl, "multiply", Multiply);

  constructor.Reset(tpl->GetFunction());
  exports->Set(Nan::New("MyObject").ToLocalChecked(), tpl->GetFunction());//类似前文提到的对象工厂构造方法,将objects对象推送出去
}

void MyObject::New(const Nan::FunctionCallbackInfo& info) {//定义MyObject对象
  if (info.IsConstructCall()) {
    // Invoked as constructor: `new MyObject(...)`
    double value = info[0]->IsUndefined() ? 0 : info[0]->NumberValue();
    MyObject* obj = new MyObject(value);
    obj->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    // Invoked as plain function `MyObject(...)`, turn into construct call.
    const int argc = 1;
    v8::Local argv[argc] = { info[0] };
    v8::Local cons = Nan::New(constructor);
    info.GetReturnValue().Set(cons->NewInstance(argc, argv));
  }
}

void MyObject::GetValue(const Nan::FunctionCallbackInfo& info) {
  MyObject* obj = ObjectWrap::Unwrap(info.Holder());
  info.GetReturnValue().Set(Nan::New(obj->value_));
}

void MyObject::PlusOne(const Nan::FunctionCallbackInfo& info) {
  MyObject* obj = ObjectWrap::Unwrap(info.Holder());
  obj->value_ += 1;
  info.GetReturnValue().Set(Nan::New(obj->value_));
}

void MyObject::Multiply(const Nan::FunctionCallbackInfo& info) {
  MyObject* obj = ObjectWrap::Unwrap(info.Holder());
  double multiple = info[0]->IsUndefined() ? 1 : info[0]->NumberValue();

  v8::Local cons = Nan::New(constructor);

  const int argc = 1;
  v8::Local argv[argc] = { Nan::New(obj->value_ * multiple) };

  info.GetReturnValue().Set(cons->NewInstance(argc, argv));
}

在addon.cc中只需要定义初始化函数

#include 
#include "myobject.h"

void InitAll(v8::Local exports) {
  MyObject::Init(exports);
}

NODE_MODULE(addon, InitAll)

 

最后完成对对象的调用addon.js

var addon = require('bindings')('addon');

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

console.log( obj.multiply().value() ); // 13
console.log( obj.multiply(10).value() ); // 130

var newobj = obj.multiply(-1);
console.log( newobj.value() ); // -13
console.log( obj === newobj ); // false

 

你可能感兴趣的:(node.js)