热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【Node】AddonC++模块开发

NodeAddonNodeAddon是官方的C++模块开发途径,可实现Node与底层技术结合,如驱动、操作系统等。开发环境需要安装nodev8.11.1npmv5.6.0node-
Node Addon

Node Addon是官方的C++模块开发途径,可实现Node与底层技术结合,如驱动、操作系统等。

开发环境

需要安装

  • node v8.11.1
  • npm v5.6.0
  • node-gyp
  • python 2.7
  • VS2017 Community C/C++桌面开发(或 windwos-build-tools)
  • VSCode 或其他IDE

安装步骤

  • 1.安装node、npm(略)
  • 2.安装python 2.7(略)

# npm中设置python环境变量
npm config set python=C:\\Python27\\python.exe

  • 3.安装VS2017 Community C/C++桌面开发(略)

# npm设置vs版本环境变量
npm config set msvs_version=2017

  • 4.安装node-gyp

# 全局安装node-gyp
npm install -g node-gyp

  • 5.VSCode 或其他IDE(略)

Hello World

从创建一个空项目文件夹开始

mkdir helloworld-addon
cd helloworld-addon
npm init
# 一顿回车
# 安装addon开发所需要的基础模块
npm install --save nan bindings

开始开发C++模块

创建一个源文件![hello.cc](), 编写如下代码:

// @file: hello.cc
// @author: jialun.liu
// @date: 2018-09-29
#include
#include
#include
using namespace std;
using namespace Nan;
using namespace v8;
void hello(const Nan::FunctionCallbackInfo &info)
{
if (info.Length() <1 || !info[0]->IsString())
{
Nan::ThrowTypeError("Illegal argument: hello('hello')");
return;
}
v8::String::Utf8Value input(info[0]->ToString());
std::string output(*input, input.length());
output = output + " world";
// create string and return
Local str = Nan::New(output).ToLocalChecked();
info.GetReturnValue().Set(str);
}
void Init(v8::Local exports)
{
// export functions,like in nodejs: module.exports.hello = hello
exports->Set(Nan::New("hello").ToLocalChecked(),
Nan::New(hello)->GetFunction());
}
// self registry
NODE_MODULE(hello, Init)

编译配置

创建编译配置文件binding.gyp,类似Makefile,注意这是一个python格式文件

# -*- coding: UTF-8 -*-
# @file:binding.gyp
# @author:jialun.liu
# @date:2018-9-29
{
# 跨操作系统配置,如不同操作系统可能链接不同的库,使用不同的编译参数
"conditions":[
[
'OS=="linux"',
{
"targets":[{
# 编译后模块名称
"target_name":"hello",
# 源文件,如有多个一一列举
"source":["hello.cc"],
"include_dirs":[
# 这里加入我们需要查找的头文件路径,nan是必须的
" ],
"link_settings":{
# 链接库设置,如我们需要链接其他的动态库、静态库
"libraries":[
# 与gcc编译参数一样,区别是要指定链接库完整路径,网上说使用-L参数可指定libpath,我试过不好使
# <(module_root_dir)是指模块路径,这样模块在被别的模块安装时就可以找到需要的库了
# "-l<(module_root_dir)/lib/libXXX.so"
]
}
}]
}
],[
'OS=="win"',
{
"targets":[{
# 编译后模块名称
"target_name":"hello",
# 源文件,如有多个一一列举
"source":["hello.cc"],
"include_dirs":[
# 这里加入我们需要查找的头文件路径,nan是必须的
" ],
"link_settings":{
# 链接库设置,如我们需要链接其他的动态库、静态库
"libraries":[
# 与gcc编译参数一样,区别是要指定链接库完整路径,网上说使用-L参数可指定libpath,我试过不好使
# <(module_root_dir)是指模块路径,这样模块在被别的模块安装时就可以找到需要的库了
# 与linux差别是不需要指定后缀名,会自动找XXX.lib,也是因为这个才需要区别操作系统
# "-l<(module_root_dir)/lib/XXX"
]
}
}]
}
]
]
}

package.json

设置package.json,注意 &#8220;gypfile&#8221;:true&#8220;main:build/Release/hello&#8221;

{
"name": "helloworld-addon",
"version": "1.0.0",
"description": "",
"main": "build/Release/hello",
"scripts": {
"test": "node hello-test.js"
},
"gypfile": true,
"author": "jialun.liu",
"license": "ISC",
"dependencies": {
"bindings": "^1.3.0",
"nan": "^2.11.0"
}
}

编译

node-gyp clean
node-gyp configure
node-gyp build
# 合并只需执行rebuild
node-gyp rebuild

编译如果没有错误就可以进行测试了

测试

创建测试hello-test.js

const hello = require('bindings')('hello');
let ret = hello.hello();
// should output: world
console.log(ret);

进阶

Addon中C++与Node数据类型转换问题比较麻烦,总结几个比较常见的数据类型

String/char */std::string

// 创建NodeString
char * str = "some string";
v8::Local nodeStr = Nan::New(str).ToLocalChecked();
// 转化NodeString
// 从参数中读取
v8::String::Utf8Value nodeStr(info[0]->ToString());
// 转为std::string
std::string stdStr(*nodeStr, nodeStr.length());
// 转为char*
char * cStr = stdStr.c_str();

数值类型

包括浮点数、整数。不支持64位整数。

Number/double

// 创建
double d = 123456.789;
v8::Local nd = Nan::New(d);
// 从参数读取
double d = info[0]->NumberValue();

Int32/int、UInt32/unsigned int

// 创建
int i32 = 123456;
v8::Local ni32 = Nan::New(i32);
unsigned ui32 = 0xffffffff;
v8::Local nui32 = Nan::New(ui32);
// 从参数读取
int32_t i32 = info[0]->Int32Value();
uint32_t u32 = info[1]->Uint32Value();

Buffer/char *

// 创建长度为8的Buffer
v8::Local buf = Nan::NewBuffer(8).ToLocalChecked();
// 获取buffer指针
long long * bp = node::Buffer::Data(buf);
// 操作指针
*bp = 0x12345678;
// 从参数读取
char * p = (char *)node::Buffer::Data(info[0]->ToObject());

Array/XX[]

// 创建一个长度为10的数组
v8::Local array = Nan::New(10);
// 为数组赋值
Nan::Set(array, 0, Nan::New("first"));
Nan::Set(array, 1, Nan::New("second"));
// TODO: 从参数读取
// v8::Local array = Nan::Cast(info[0]->ToObject());

踩坑

  • 1.c++代码中不要出现中文注释,否则编码错误会导致意想不到的问题,比如莫名某些代码会不生效(是因为编码问题导致编译器识别换行错误,代码被认为注释掉了,切记!!!)

https://github.com/liujialun/&#8230;


推荐阅读
author-avatar
木色雪魂K
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有