nodejs 用 C++ 开发插件, 带你入门

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, 完全安装.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值