【Node】Addon C++模块开发
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 <nan.h> #include <string.h> #include <stdio.h> using namespace std; using namespace Nan; using namespace v8; void hello(const Nan::FunctionCallbackInfo<v8::Value> &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<v8::String> str = Nan::New(output).ToLocalChecked(); info.GetReturnValue().Set(str); } void Init(v8::Local<v8::Object> exports) { // export functions,like in nodejs: module.exports.hello = hello exports->Set(Nan::New("hello").ToLocalChecked(), Nan::New<v8::FunctionTemplate>(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是必须的 "<!(node -e \"require('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是必须的 "<!(node -e \"require('nan')\")" ], "link_settings":{ # 链接库设置,如我们需要链接其他的动态库、静态库 "libraries":[ # 与gcc编译参数一样,区别是要指定链接库完整路径,网上说使用-L参数可指定libpath,我试过不好使 # <(module_root_dir)是指模块路径,这样模块在被别的模块安装时就可以找到需要的库了 # 与linux差别是不需要指定后缀名,会自动找XXX.lib,也是因为这个才需要区别操作系统 # "-l<(module_root_dir)/lib/XXX" ] } }] } ] ] }
package.json
设置package.json,注意 "gypfile":true 和 "main:build/Release/hello"
{ "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<v8::String> 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<v8::Number> nd = Nan::New(d); // 从参数读取 double d = info[0]->NumberValue();
Int32/int、UInt32/unsigned int
// 创建 int i32 = 123456; v8::Local<v8::Int32> ni32 = Nan::New(i32); unsigned ui32 = 0xffffffff; v8::Local<v8::Uint32> nui32 = Nan::New(ui32); // 从参数读取 int32_t i32 = info[0]->Int32Value(); uint32_t u32 = info[1]->Uint32Value();
Buffer/char *
// 创建长度为8的Buffer v8::Local<v8::Object> 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> array = Nan::New<Array>(10); // 为数组赋值 Nan::Set(array, 0, Nan::New("first")); Nan::Set(array, 1, Nan::New("second")); // TODO: 从参数读取 // v8::Local<Array> array = Nan::Cast<v8::Array>(info[0]->ToObject());
踩坑
- 1.c++代码中不要出现中文注释,否则编码错误会导致意想不到的问题,比如莫名某些代码会不生效(是因为编码问题导致编译器识别换行错误,代码被认为注释掉了,切记!!!)
相关推荐
boneix 2020-10-21
seanzed 2020-10-15
ifconfig 2020-10-14
学留痕 2020-09-20
往后余生 2020-09-17
kka 2020-09-14
redis 2020-09-07
lzccheng 2020-09-06
soyo 2020-08-31
stonerkuang 2020-08-18
LxyPython 2020-08-17
raksmart0 2020-08-17
Lzs 2020-08-14
MrHaoNan 2020-07-31
80530895 2020-07-05
lengyu0 2020-06-28
YarnSup 2020-06-28
huanglianhuabj00 2020-06-27