天天看点

python c++ 混合编程_简单上手nodejs调用c++(c++和js的混合编程)。

因为项目的原因,最近经常使用node.js搭RESTful接口。

性能还是很不错啦,感觉比Spring Boot之类的要快。而且在不错的性能之外,只要程序结构组织好,别让太多的回调把程序结构搞乱,整体开发效率比Java快的就太多了。

如果想进一步提高效率,使用c++来优化部分模块是不错的选择。尤其可贵的是nodejs对于同c++的混合编程支持的很好,个人感觉跟写Python的扩展模块处于同样的易用水平。

我们从Hello World开始:

首先要有一个空白的工作目录,在其中建立一个node包管理文件package.json,内容为:

{

"name": "test-cpp-module",

"version": "0.1.0",

"private": true,

"gypfile": true

}

随后在目录中执行命令:npm install node-addon-api --save安装nodejs扩展模块的开发支持包。这里假设你已经安装配置好了nodejs和相应的npm包管理工具,还有xcode的相关命令行编译工具。我们不重复这些基本工具的安装配置,需要的话请参考官网相关文档。

上面命令执行完成,我们就完成了基本开发环境的配置。

c++的模块由binding.gyp文件描述,并完成自动编译的相关配置工作,我们新建一个binding.gyp文件,内容为:

{

"targets": [

{

"target_name": "democpp",

"sources": [

"democpp.cc"

],

"include_dirs": [

"

using namespace Napi;

String Hello(const CallbackInfo& info) {

return String::New(info.Env(), "world");

}

Napi::Object Init(Env env, Object exports) {

exports.Set("hello", Function::New(env, Hello));

return exports;

}

NODE_API_MODULE(addon, Init)

程序中引入napi.h头文件,使用Napi的namespace还有最后的NODE_API_MODULE(addon,Init)都是模板化的,照抄过来不用动。

Init函数中,使用exports.Set()引出要暴露给nodejs调用的函数。如果有多个需要引出的函数,就写多行。

Hello函数是我们主要完成工作的部分,本例中很简单,只是用字符串的方式返回一个“world”。

以上democpp.cc/binding.gyp/package.json三个文件准备好之后,在命令行执行:npm install,顺利的话会得到这样的输出信息:

$ npm install

> test-cpp-mod[email protected] install /home/andrew/Documents/dev/html/nodejs/callcpp

> node-gyp rebuild

SOLINK_MODULE(target) Release/nothing.node

CXX(target) Release/obj.target/democpp/democpp.o

SOLINK_MODULE(target) Release/democpp.node

这表示编译顺利完成了,如果碰到错误,可以根据错误信息去判断解决方案。通常都是环境配置缺少相关程序或者上述的三个文件有打字错误。

下面我们验证一下模块的编译结果,在命令行使用nodejs,引入编译的模块文件,然后调用hello函数来看看:

> $ node

> democpp=require("./build/Release/democpp.node")

{ hello: [Function] }

> democpp.hello()

'world'

>

上面是最简单的一个范例,下面我们增加一点难度。在GNU的环境下,通常我们的程序都会包含很多第三方的扩展库,我们这里再举一个调用openssl的例子:

package.json文件不用修改,我们不需要在nodejs层面增加新的依赖包。

编译带第三方扩展库的c++程序,通常需要在编译时指定额外的头文件包含路径和链接第三方库,这些都是在binding.gyp中指定的,这些指定在nodejs自动编译的时候,会解析并应用在命令行的编译工具中。

{

"targets": [

{

"target_name": "democpp",

"sources": [

"democpp.cc"

],

"include_dirs": [

"

#include using namespace Napi;

void openssl_md5(const char *data, int size, unsigned char *buf){

MD5_CTX c;

MD5_Init(&c);

MD5_Update(&c,data,size);

MD5_Final(buf,&c);

}

String GetMD5(const CallbackInfo& info) {

Env env = info.Env();

std::string password = info[0].As().Utf8Value();

//printf("md5 in str:%s %ld\n",password.c_str(),password.size());

unsigned char hash[16];

memset(hash,0,16);

openssl_md5(password.c_str(),password.size(),hash);

char tmp[3];

char md5str[33]={};

int i;

for (i = 0; i < 16; i++){

sprintf(tmp,"%02x",hash[i]);

strcat(md5str,tmp);

}

return String::New(env, md5str,32);

}

String Hello(const CallbackInfo& info) {

return String::New(info.Env(), "world");

}

Napi::Object Init(Env env, Object exports) {

exports.Set("hello", Function::New(env, Hello));

exports.Set("md5", Function::New(env, GetMD5));

return exports;

}

NODE_API_MODULE(addon, Init)

为了工作方便,源码中增加了一个没有引出的openssl_md5函数,仅供程序内部使用。因为没有引出,nodejs并不知道这个函数的存在。

从nodejs传递参数给c++的函数,是使用info[0].As().Utf8Value()这样的形式。返回值到nodejs在hello函数中就已经看过了。

各项修改完成,同样回到命令行使用npm install重新编译。编译的过程和信息略,我们直接看调用的测试:

> $ node

> democpp=require("./build/Release/democpp.node")

{ hello: [Function], md5: [Function] }

> democpp.hello()

'world'

> democpp.md5("abc")

'900150983cd24fb0d6963f7d28e17f72'

>

想验证一下计算的正确性?可以直接执行openssl试试:

$ echo -n "abc" | openssl md5

900150983cd24fb0d6963f7d28e17f72

嗯,无悬念的相同。

参考文档:

https://github.com/kriasoft/nodejs-api-starter

https://github.com/nodejs/node-addon-api/blob/master/doc/node-gyp.md