通过 SWIG 构建 Node.js Native c++工程

介绍

有人说javascript是最优美的语言,确实也是。然而,了解过javascript发展史的人都知道,在早年的浏览器大战中能够脱颖而出,也绝非易事。更何况,javascript的标准化过程也是曲折艰难。然而,自从Ryan Dahl在2009年js峰会上介绍了Node.js以后,让程序员们大开眼界。Node.js融合了google的v8 javascript引擎,引入了服务器端常用的事件处理机制,使得javascript语言也可以用来实现服务端程序,前端程序员写一个服务器不再是一件空难的事情。当然,业界有很多项目也尝试用Node.js来实现服务器逻辑。

然而,javascript毕竟是脚本语言,在一些计算密集型,实时性要求高的产品中,采用Native(主要是通过c++)实现这些逻辑显得尤为重要。

Node.js本身常用的一种叫Nan(Native Abstract for Node.js)的方式,非常不错。其github的项目地址:https://github.com/nodejs/nan

而,本文要介绍的是通过SWIG来实现javascript和c++的融合。

SWIG

介绍

SWIG的官网地址是:http://www.swig.org

SWIG主要是一个接口编译器,将C/C++程序暴露的API描述在一个后缀是(.i)的文件里面,然后通过swig编译器生成c++源代码的一个wrapper,

生成的代码可以在脚本语言的解释器平台上执行,从而实现了脚本语言调用c/c++程序API的目的。

SWIG只是代码生成工具,不需要依赖任何平台和库。

SWIG支持的脚本语言有很多:比如tcl,java,php5,php7,python等很多,当然也包含javascript了。

SWIG项目的意义,以及很多优势在官网里面有详细的介绍,里面也有很多参考资源,可以方便进一步的学习。

本文我们只是通过一个简单的demo来说明,swig的基本用法。

安装

目前支持SWIG的系统有windows,mac,cygwin,linux,等很多unix-like系统。

作者本人用的是cygwin环境。

具体安装过程步骤相对简单,参考官网说明即可。

安装完成后,打开命令行做如下的测试:

   $ swig -version

    SWIG Version 3.0.12

    Compiled with g++ [x86_64-unknown-cygwin]

    Configured options: +pcre

    $ swig --help
     Target Language Options
     -allegrocl      - Generate ALLEGROCL wrappers

SWIG对C/C++的支持

因为SWIG主要目的是实现不同语言的互通,所以并没有完全支持c++语言的所有特性。比如,c++的多重继承,运算符重载,成员函数重载等。

当然,基本常用的语法都是支持,比如虚函数,多态,还有c++11新标准也支持。

接口文件说明

swig的接口文件如下,文件后缀名是(.i).

  1 %module example
  2 %{
  3 #include "c_example.h"
  4 #include "shape.h"
  5 %}
  6
  7 extern void print_c();
  8 extern const char *get_c_desc();
  9
 10 %include "shape.h"
  

%module : 是模块说明指令,这个是必须的,我们在node.js中通过require引入需要的。

%{ %} : 中包含的内容,swig是不做任何处理的,直接导入到最终生成的文件中。这个类似yacc/bison的的%{ %}指令。所以,我们是可以在这个指令块内,写一些C/C++代码的。例子中 c_example.h是一个c语言API的声明。shape.h是一个c++头文件的声明,包含了继承,多态,静态成员变量等特性。

第7-8行:是暴露给脚本语言访问的的接口。这里其实就是一个wrapper。

第10 行:用了%include 指令,这是将shape.h中的所有接口都暴露给脚本语言。如果说你原始头文件的所有接口都暴露,可以直接这样包含。如果,原始头文件中包含的内容太多不想暴露,或者是包含了swig不支持的语法,需要像7-8行那样,做一个wrapper。

具体操作

swig代码生成

写完接口文件,我们就可以用swig生成代码。如下,

swig.exe -c++ -javascript -node example.i

执行完上面的命令会在当前目录下面生成一个example_wrap.cxx的文件。

-c++ : 只是表示native代码是c++代码,这里需要说明的是C/C++语言都需要增加此选项。

-javascript: 表示生成的脚本语言是javascript语言。

-node:是执行javascript的引擎。

最后是接口文件名称。

特别说明:这里的c/c++语言后缀必须是c++语言的文件后缀,比如(*.cxx, *.cpp)等,否则编译的时候会出错。

构建Native编译工程

这里怎么构建Node.js工程,以及package.json的说明就不再讲了。

构建Node.js native 编译, 创建一个swig-js工程目录,结构如下:

swig-js
│  binding.gyp //Native build 文件
│  index.js //js测试文件
│  package-lock.json
│  package.json
└─src
     | c_example.cxx
     | c_example.h
     | example.i // 接口文件
     | example_wrap.cxx // swig生成的文件
     | shape.cxx
     | shape.h

首先, 创建binding.gyp文件 , 内容如下:

{
  "targets": [
    {
      "target_name": "example",
      "include_dirs":
      [
          'src'
      ],
      "sources":
       [
            "src/c_example.cxx",
            "src/shape.cxx",
            "src/example_wrap.cxx"
       ]
    }
  ]
}
其次,编译, 经过下面过程的编译,就生成了native代码。
   npm install node-gyp -g //安装native build工具

   node-gyp configure

   node-gyp build

最后,测试,index.js测试代码如下:


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

example.print_c();

console.log(example.get_c_desc());

var shape = new example.Circle(2);

shape.move(2, 3);

let square = new example.Square(5);

console.log('The square area='+square.area());

执行:

$ node index.js
I'm c swig test.
Test c module
Move from(0, 0) to (2, 3)
The square area=25
经过上面的步骤,通过swig 进行javascript和c++的融合开发就完成了。

特别说明

从node7.0开始,删除了一些api,比如, v8::Object::GetHiddenValue 。因为swig的开发目前还有支持最新的的node版本,所以需要node 7.0以下的版本来测试。

笔者用的是v6.17.1 node版本。

样例代码路径:https://github.com/avdance/AVDemo/tree/master/code/swig-js

你可能感兴趣的:(通过 SWIG 构建 Node.js Native c++工程)