nodejs 用 C++ 开发插件, 带你入门
为了顺利理解本文, 希望读者:
- 有 C ++ 开发 基础
- 能看懂 nodejs 源码
- 能做基本的 npm 开发
本文的 demo 例子 在
https://gitee.com/linuxmail/node-addon-api-study
$ npm i
$ npm run build # 或
# npx node-gyp rebuild
编译后的结果在
build/Release/hello.node
本 demo 例子去掉了所有的干扰信息, 保证你一遍就会
对比 test.js 和 hello.cc, 一块儿看
ps: 封装C+类成本高, 完全可以在js层实现
package.json
{
"name": "node-addon-demo",
"version": "2.0.0",
"description": "Node.js Addons Demo",
"main": "test.js",
"private": true,
"dependencies": {
"node-addon-api": "^5.1.0",
"node-gyp": "^10.1.0"
},
"scripts": {
"build": "node-gyp rebuild",
"test": "node test.js"
}
}
hello.cc
#include <napi.h>
Napi::Value hello(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
return Napi::String::New(env, "hello-world");
}
Napi::Value addNumber(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
int64_t arg0 = info[0].As<Napi::Number>().ToNumber().Int64Value();
int64_t arg1 = info[1].As<Napi::Number>().ToNumber().Int64Value();
int64_t sum = arg0 + arg1;
Napi::Number result = Napi::Number::New(env, sum);
return result;
}
Napi::Value createObject(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::Object result = Napi::Object::New(env);
result.Set(Napi::String::New(env, "one"), Napi::String::New(env, "ooo"));
result.Set(Napi::String::New(env, "two"), Napi::Number::New(env, 222));
return result;
}
Napi::Value createArray(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::Array result = Napi::Array::New(env);
result.Set(Napi::Number::New(env, 0), Napi::String::New(env, "ooo"));
Napi::Object obj = Napi::Object::New(env);
obj.Set(Napi::String::New(env, "one"), Napi::String::New(env, "ooo"));
obj.Set(Napi::String::New(env, "two"), Napi::Number::New(env, 222));
result.Set(Napi::Number::New(env, 1), obj);
result.Set(Napi::Number::New(env, 2), obj);
return result;
}
Napi::Value runCallback(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::Function cb = info[0].As<Napi::Function>();
cb.Call(env.Global(), {});
return Napi::Boolean::New(env, true);
}
class createClass;
class createClass : public Napi::ObjectWrap<createClass>
{
public:
createClass(const Napi::CallbackInfo &info);
~createClass();
public:
Napi::Value set(const Napi::CallbackInfo &info);
Napi::Value get(const Napi::CallbackInfo &info);
protected:
int times_{0};
};
createClass::createClass(const Napi::CallbackInfo &info) : Napi::ObjectWrap<createClass>(info)
{
info.This().As<Napi::Object>().Set("myValue", Napi::String::New(this->Env(), ""));
}
createClass::~createClass()
{
}
Napi::Value createClass::set(const Napi::CallbackInfo &info)
{
Napi::Value last = info.This().As<Napi::Object>().Get("myValue");
info.This().As<Napi::Object>().Set("myValue", info[0]);
return last;
}
Napi::Value createClass::get(const Napi::CallbackInfo &info)
{
Napi::Value last = info.This().As<Napi::Object>().Get("myValue");
return last;
}
Napi::Object Init(Napi::Env env, Napi::Object exports)
{
exports.Set(Napi::String::New(env, "hello"), Napi::Function::New(env, hello));
exports.Set("addNumber", Napi::Function::New(env, addNumber));
exports.Set("createObject", Napi::Function::New(env, createObject));
exports.Set("createArray", Napi::Function::New(env, createArray));
exports.Set("runCallback", Napi::Function::New(env, runCallback));
// auto t = DefineClass(env, "createClass", properties, (void *)NULL);
auto t = createClass::DefineClass(env, "createClass", {createClass::InstanceMethod("set", &createClass::set, napi_default_method), createClass::InstanceMethod("get", &createClass::get, napi_default_method)}, (void *)NULL);
Napi::FunctionReference *constructor = new Napi::FunctionReference();
*constructor = Napi::Persistent(t);
env.SetInstanceData<Napi::FunctionReference>(constructor);
exports.Set("createClass", t);
return exports;
}
NODE_API_MODULE(test, Init)
test.js
let addon = require('./build/Release/hello.node');
console.log(addon.hello());
console.log(addon.addNumber(1.0, 123.123));
console.log(addon.addNumber(1.0, "12311"));
let obj = addon.createObject();
obj.bbbb = 123;
console.log(obj);
let arr = addon.createArray();
arr.push("str");
console.log(arr);
addon.runCallback(
() => {
console.log("I AM callback");
}
);
let cl = new addon.createClass();
console.log("class set 123, old::", cl.set('123'));
console.log("S", cl)
console.log("class set '456', old:", cl.set('456'));
console.log("class set '666', old:", cl.set('666'));
console.log("class set 777, old type:", typeof cl.set(777));
console.log("class set 888, old type:", typeof cl.set(888));
console.log("class get, value:", cl.get());
console.log("class get, type:", typeof cl.get());
delete cl;
binding.gyp
{
"targets": [
{
"target_name": "hello",
# -fno-exceptions 忽略掉编译过程中的一些报错
"cflags!": [ "-fno-exceptions" ],
# 下面这句话, 支持源码是 UTF-8 字符集
"cflags_cc!": ["-fno-exceptions", "-finput-charset=UTF-8", '-std=c++11'],
"sources": [ "hello.cc" ],
"include_dirs": [ # 头文件搜索路径
"<!@(node -p \"require('node-addon-api').include\")",
],
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
# 全局库
'libraries': [
],
"conditions": [
[
'OS == "linux"',
{
"include_dirs": [
"<!@(node -p \"require('node-addon-api').include\")",
# "./src/",
],
"libraries": [
],
},
'OS == "mac"',
{
"include_dirs": [
# "./src/",
],
"libraries": [
],
},
'OS == "win"',
{
"include_dirs": [
# "./src/",
],
"libraries": [
],
'msvs_settings': {
'VCCLCompilerTool': {
'runtimeLibrary': 0,
},
"VCCLCompilerTool": {
"AdditionalOptions": [
"/utf-8"
]
},
}
}
]
]
}
]
}
windows平台
windows平台的一个坑是, 没编译环境.
下载node, 完全安装.