Back to home

NodeJS C++ Extension

NodeJs除了带给我们高效的Http事物处理,由于基于V8引擎,所以自身就具有良好的拓展性.
下面,我将会实现一个简单的NodeJs本地拓展例子.

HelloWorld in JS

在此之前,我们先将我们打算实现的拓展,先用原生JS实现一遍.

HelloWorldJs = function() {
  this.m_count = 0;
};

HelloWorldJs.prototype.hello = function()
{
  this.m_count++;
  return "Hello World";
};
HelloWorldJs.prototype.out = function()
{
  return this.m_count;
};

exports.HelloWorldJs = HelloWorldJs;

可以看到,我们实现了一个Helloworld的构造函数.它具有两个实例方法: hello 以及 out.
我们可以这样使用它.

var helloworld = require('helloworld_js');
var hi = new helloworld.HelloWorldJs();
console.log(hi.hello()); // output: "Hello World"
console.log(hi.out()); //output: 1 

HelloWorld C++拓展

实际上,关于NodeJS的拓展,就是关于V8的拓展.所以最直接的方式,是定义一个V8环境中的ObjectWrap子类.然后实现其自身方法. 你可以在这里获得更多关于V8拓展的开发信息.

首先,我们需要导入一些必要的头文件.为了程序更简洁,我引入了他们的名称空间.

#include <v8.h>
#include <node.h>

using namespace node;
using namespace v8;

随后,我声明了一个继承自ObjectWrap的对象,它提供几个常用方法,就像JavaScript中所有东西都是对象一样,你的类应该集成自它.
我还声明了一个私有变量m_count,它统计了我调用了多少次hello方法.

class HelloWorld: ObjectWrap
{
private:
  int m_count;

接下来,为我的类定义了一些供JavaScript环境初始化的一些信息.

public:    
  static Persistent<FunctionTemplate> s_ct;
  static void Init(Handle<Object> target)
  {
    HandleScope scope;

    Local<FunctionTemplate> t = FunctionTemplate::New(New);

    s_ct = Persistent<FunctionTemplate>::New(t);
    s_ct->InstanceTemplate()->SetInternalFieldCount(1);
    s_ct->SetClassName(String::NewSymbol("HelloWorld"));

    NODE_SET_PROTOTYPE_METHOD(s_ct, "hello", Hello);
    NODE_SET_PROTOTYPE_METHOD(s_ct, "out", Out);

    target->Set(String::NewSymbol("HelloWorld"),
                s_ct->GetFunction());
  }

我先创建一个 Persistent,然后设置一些参数.
我将构造函数的名称设置为了"HelloWorld",并且使用NODESET_PROTOTYPE_METHOD宏,为其添加了两个方法. 最后将这个对象导出到module(也就是target)的"HelloWorld"属性中,对吧,这些HelloWorld维护了一致性,虽然你会因此有些无法区分,没关系,仔细看看,你会发现其中差别的.^^

照例,这是一个C++程序.

  HelloWorld() :
    m_count(0)
  {
  }

  ~HelloWorld()
  {
  }

C++ 对象构造完毕后,当你的类(C++中)被V8环境调用时,他会调用你的::New方法.
当Javascript中使用new操作符,对你的构造函数进行调用时,下面的方法将会被调用.而其中新创建的对象将会以args.This()的形式传递进去.是不是有点像Python?实例的方法是通过传递实例本身.

  static Handle<Value> New(const Arguments& args)
  {
    HandleScope scope;
    HelloWorld* hw = new HelloWorld();
    hw->Wrap(args.This());
    return args.This();
  }

下面就是你定义的方法了,别忘了,你已经将他们绑定到你的prototype中去了.

  static Handle<Value> Hello(const Arguments& args)
  {
    HandleScope scope;
    HelloWorld* hw = ObjectWrap::Unwrap<HelloWorld>(args.This());
    hw->m_count++;
    Local<String> result = String::New("Hello World");
    return scope.Close(result);
  }

  static Handle<Value> Out(const Arguments& args)
  {
    HandleScope scope;
    HelloWorld* hw = ObjectWrap::Unwrap<HelloWorld>(args.This());
    Local<Number> result = Number::New(hw->m_count);
    return scope.Close(result);

  }

};

Persistent<FunctionTemplate> HelloWorld::s_ct;

最后,我们需要声明一下,以便Node能够找到我们.

extern "C" {
  static void init (Handle<Object> target)
  {
    HelloWorld::Init(target);
  }

  NODE_MODULE(helloworld, init);
}

自此,已经完成了全部逻辑代码的编写.
最后需要一点儿用于构建的脚本.

#File wscript def set_options(opt): opt.tool_options("compiler_cxx")

def configure(conf):
  conf.check_tool("compiler_cxx")
  conf.check_tool("node_addon")

def build(bld):
  obj = bld.new_task_gen("cxx", "shlib", "node_addon")
  obj.cxxflags = ["-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE", "-Wall"]
  obj.target = "helloworld"
  obj.source = "helloworld.cc"

在控制台中输入node-waf configure && node-waf build就能够帮助我们自动完成构建. 试试:

> hl = require("./build/default/helloworld")
{ HelloWorld: [Function: HelloWorld] }
> a = new hl.HelloWorld()
{}
> a.out()
0
> a.hello()
'Hello World'
> a.out()
1
>

引用自: https://www.cloudkick.com/blog/2010/aug/23/writing-nodejs-native-extensions/
你可以在这里找到所有代码:http://github.com/pquerna/node-extension-examples