Node.js 添加 C-C++ Addon

来源:互联网 发布:双叶实木家具知乎 编辑:程序博客网 时间:2024/05/03 20:01

一直想要开一个博客,总结记录一下自己学到的东西,今天终于动笔写了第一篇,希望能够坚持下去。

我的博客主要会分享一些自己最近学习的东西,主要是给自己看的,如果能帮到别人的话当然最好了。

----------------------我是华丽的分割线-------------------------------

实验室最近正在做一个基于Node.js的项目,之前对Front End的知识了解很少,所以从JavaScript一点点学起慢慢熟悉。 我的主要任务是把一个已经写好的C语言程序转化为Node.js的Library可以随时调用运行。

按照Node官方给的文档,我从最基础的Hello World开始,学习如何添加Addon,以下是结合官方的documentation我自己的总结。

 

Node.js Addons 是C/C++写成的Objects,可以通过require()调用,用起来就像Node自带的module一样,主要用于提供介于Node.js和C/C++库之间的接口。

实现Addons所需要的背景知识主要包括:V8 JavaScript Engine, Libuv, Node.js内部library, Node.js内部库自带一些C/C++ API可以供Addons使用。

 

从最简单的开始,添加一个C++ Addon实现的功能等同于: module.exports.hello = function(){return 'world';}; 调用这个函数,可以返回字符串“world”;

 

Step1

首先需要通过运行命令’npm init‘创建一个 package.json文件的框架,用来记录所需的配置文件

Step2

安装 “NAN”。 NAN是一个介于 C++源程序,Node,和 V8 API之间的抽象层。NAN的存在是为了保证当V8的API更新后,之前的程序仍可以运行。 当V8的API更新后,只需更新NAN便可以保证已经写好的程序可以继续运行,避免了修改源代码的问题。

通过运行‘npm install nan@latest --save'来安装NAN,并将NAN作为 dependency存储在package.json里。

Step3

所有的Addons 都会使用[node-gyp]编译,在package.json中加入‘gypfile:true’来使能nodel-gyp编译。 当 node-gyp被调用时,会寻找一个与‘package.json’在同一目录下的‘binding.gyp’ file。 这个binding文件用YAML写成,用于描述build的细节,比如源文件以及所需的dependencies。node-gyp会参考我们自己写成的binding.gyp文件和一个common.gypi来进行编译,common.gypi描述了node.js会用到的设置和可能的dependencies。 因为我们的代码将会在node下编译,所以必须要有common.gypi file用于描述node的环境设置。另外,我们还需要下载和我们正在运行的版本对应的tarball。(原文是it must also download the complete tarball of the particular release you are running,我也没有很理解这句话,看懂的朋友希望指正)。

文档中给出了简单的binding文件:

{

  "targets": [

    {

      "target_name": "hello",

      "sources": [ "hello.cc" ],

      "include_dirs": [

        "<!(node -e \"require('nan')\")"

      ]

    }

  ]

}

我们只要新建一个binding.gyp file并复制这段代码就可以了。

这段代码做了如下几件事:1 命名目标Addon为“hello”,最终输出的文件名将会是hello.node. 2 需要编译的源文件是"hello.cc" 3 规定了编译时[NAN]的路径

Step4

编写hello.cc的源代码。

#include <nan.h>

 

using namespace v8;

 

NAN_METHOD(Method) {

  NanScope();

  NanReturnValue(String::New("world"));

}

 

void Init(Handle<Object> exports) {

  exports->Set(NanSymbol("hello"), FunctionTemplate::New(Method)->GetFunction());

}

 

NODE_MODULE(hello, Init)

现在从下到上来分析这段代码:

NODE_MODULE(hello,Init)定义了addon的entry-point, 参数“hello”必须与binding file中的target相同,第二个参数“Init“指向了要调用的函数。

void Init(Handle<Object> exports) {

  exports->Set(NanSymbol("hello"), FunctionTemplate::New(Method)->GetFunction());

}

按照NODE_MODULE的定义,这一函数是addon实际的entry-point。 这一函数有两个参数: ‘exports’ 与js文件中的‘module.exports'相同,第二个参数(本例中已经被忽略)是‘module’,类似js文件中的‘module’。一般情况下,我们会给exports attach一些属性,但是我们也可以使用‘module’(第二个参数)来替换‘module’的exports属性,这样就只能 export a single thing, e.g. module.exports = function(){}.

本例中, 我们只需要attach一个“hello” property 在module.exports中。所以我们给V8 的一个‘Function’对象[SET]一个 V8’String‘ 属性。 首先,我们使用 NanSymbol() 函数新建一个’symbol‘ 字符串(之后可以重复使用). 我们使用 V8 'FunctionTemplate' 把一个普通的 C++函数转换为一个 V8-callable function. 在我们的例子中,这个‘C++函数’是Method function。

NAN_METHOD(Method) {

  NanScope();

  NanReturnValue(String::New("world"));

}

这里体现出了NAN的用处。 V8 API的改变使得 将C++ Addon 添加到不同版本的node变得很困难。 所以NAN提供了一个简单的mapping,我们可以定义一个 ‘FunctionTemplate’可以接受的 V8 compatible function。 在最近的V8中,NAN_METHOD(Method) 可以扩展成 'void Method(const v8::FunctionCallbackInfo<v8::Value>& args)',这是一个 V8 callable function的标准signature。'args' 包括了 call information, 比如 JavaScript function的参数,设置返回值等。

NanScope() 定义了新建的‘handles’的生命周期。当我们在函数开始时使用NanScope时表示所有的 我们使用的V8 object存活的时间与该function相等。 如果没有这句代码,V8 Object将不会在function结束时被回收。

NanReturnValue设置了函数的返回值。在本例子中, 我们新建了一个简单的“world” 字符串,这个字符串将会被暴露为标准的JavaScript String。 

 

Step 5

编译我们的Addon。 通过‘sudo npm install node-gyp -g' 安装'node-gyp'。 

运行‘node-gyp configure' 来设置build fixtures. 会生成一个 'Makefile' 文件

运行‘node-gyp build'启动编译过程。 或者可以使用'node-gyp rebuild' 将 ‘configure‘ & ’build‘和成一步。成功以后我们就有了一个可以使用的addon binary文件。Node可以载入并运行这个文件像载入普通.js module file 一样。

 

Step 6

编写JavaScript

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

 

console.log(addon.hello());

调用addon的hello()function,输出“world”

 

 

 

0 0
原创粉丝点击