N-API类似于 NAN 项目, 但是是由 nodejs 官方维护, 从此就不需要安装外部的依赖来导入到头文件. 并且提供了可靠的抽象层
他暴露了node_api.h
头文件, 抽象了 nodejs 和包的内部实现, 每次 Nodejs 更新, N-API 就会同步进行优化保证 ABI 的可靠性
这里是 N-API 的所有接口文档, 这里是官方对 N-API 的 ABI 稳定性的描述
N-API 同时适合于 C 和 C++, 但是 C++的 API 使用起来更加的简单, 于是, node-addon-api 就应运而生.
Example
hello world
#include <assert.h>
#include <node_api.h>
napi_value Method(napi_env env, napi_callback_info info) {
napi_status status;
napi_value world;
status = napi_create_string_utf8(env, "world", 5, &world);
assert(status == napi_ok);
return world;
}
#define DECLARE_NAPI_METHOD(name, func) \
{ name, 0, func, 0, 0, 0, napi_default, 0 }
napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method);
status = napi_define_properties(env, exports, 1, &desc);
assert(status == napi_ok);
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
config building.gyp:
{
"targets": [
{
"target_name": "hello",
"sources": [ "hello.cc" ]
}
]
}
Test with js:
var addon = require('bindings')('hello');
console.log(addon.hello()); // 'world'
function arguments
#include <assert.h>
#include <node_api.h>
#include <stdio.h>
napi_value Add(napi_env env, napi_callback_info info) {
napi_status status;
size_t argc = 2;
napi_value args[2];
status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
assert(status == napi_ok);
if (argc < 2) {
napi_throw_type_error(env, nullptr, "Wrong number of arguments");
return nullptr;
}
napi_valuetype valuetype0;
status = napi_typeof(env, args[0], &valuetype0);
assert(status == napi_ok);
napi_valuetype valuetype1;
status = napi_typeof(env, args[1], &valuetype1);
assert(status == napi_ok);
if (valuetype0 != napi_number || valuetype1 != napi_number) {
napi_throw_type_error(env, nullptr, "Wrong arguments");
return nullptr;
}
double value0;
status = napi_get_value_double(env, args[0], &value0);
assert(status == napi_ok);
double value1;
status = napi_get_value_double(env, args[1], &value1);
assert(status == napi_ok);
napi_value sum;
status = napi_create_double(env, value0 + value1, &sum);
assert(status == napi_ok);
return sum;
}
#define DECLARE_NAPI_METHOD(name, func) \
{ name, 0, func, 0, 0, 0, napi_default, 0 }
napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_property_descriptor addDescriptor = DECLARE_NAPI_METHOD("add", Add);
status = napi_define_properties(env, exports, 1, &addDescriptor);
assert(status == napi_ok);
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
var addon = require('bindings')('addon.node')
console.log('This should be eight:', addon.add(3, 5))
callbacks
#include <assert.h>
#include <node_api.h>
napi_value RunCallback(napi_env env, const napi_callback_info info) {
napi_status status;
size_t argc = 1;
napi_value args[1];
status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
assert(status == napi_ok);
napi_value cb = args[0];
napi_value argv[1];
status = napi_create_string_utf8(env, "hello world", NAPI_AUTO_LENGTH, argv);
assert(status == napi_ok);
napi_value global;
status = napi_get_global(env, &global);
assert(status == napi_ok);
napi_value result;
status = napi_call_function(env, global, cb, 1, argv, &result);
assert(status == napi_ok);
return nullptr;
}
napi_value Init(napi_env env, napi_value exports) {
napi_value new_exports;
napi_status status = napi_create_function(
env, "", NAPI_AUTO_LENGTH, RunCallback, nullptr, &new_exports);
assert(status == napi_ok);
return new_exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
var addon = require('bindings')('addon');
addon(function(msg){
console.log(msg); // 'hello world'
});
object_factory
#include <assert.h>
#include <node_api.h>
napi_value CreateObject(napi_env env, const napi_callback_info info) {
napi_status status;
size_t argc = 1;
napi_value args[1];
status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
assert(status == napi_ok);
napi_value obj;
status = napi_create_object(env, &obj);
assert(status == napi_ok);
status = napi_set_named_property(env, obj, "msg", args[0]);
assert(status == napi_ok);
return obj;
}
napi_value Init(napi_env env, napi_value exports) {
napi_value new_exports;
napi_status status = napi_create_function(
env, "", NAPI_AUTO_LENGTH, CreateObject, nullptr, &new_exports);
assert(status == napi_ok);
return new_exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
var addon = require('bindings')('addon');
var obj1 = addon('hello');
var obj2 = addon('world');
console.log(obj1.msg+' '+obj2.msg); // 'hello world'
object_wrap
// addon.cc
#include "myobject.h"
napi_value Init(napi_env env, napi_value exports) {
return MyObject::Init(env, exports);
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
// myObject.cc
#include "myobject.h"
#include <assert.h>
napi_ref MyObject::constructor;
MyObject::MyObject(double value)
: value_(value), env_(nullptr), wrapper_(nullptr) {}
MyObject::~MyObject() {
napi_delete_reference(env_, wrapper_);
}
void MyObject::Destructor(napi_env env,
void* nativeObject,
void* /*finalize_hint*/) {
reinterpret_cast<MyObject*>(nativeObject)->~MyObject();
}
#define DECLARE_NAPI_METHOD(name, func) \
{ name, 0, func, 0, 0, 0, napi_default, 0 }
napi_value MyObject::Init(napi_env env, napi_value exports) {
napi_status status;
napi_property_descriptor properties[] = {
{"value", 0, 0, GetValue, SetValue, 0, napi_default, 0},
DECLARE_NAPI_METHOD("plusOne", PlusOne),
DECLARE_NAPI_METHOD("multiply", Multiply),
};
napi_value cons;
status = napi_define_class(
env, "MyObject", NAPI_AUTO_LENGTH, New, nullptr, 3, properties, &cons);
assert(status == napi_ok);
status = napi_create_reference(env, cons, 1, &constructor);
assert(status == napi_ok);
status = napi_set_named_property(env, exports, "MyObject", cons);
assert(status == napi_ok);
return exports;
}
napi_value MyObject::New(napi_env env, napi_callback_info info) {
napi_status status;
napi_value target;
status = napi_get_new_target(env, info, &target);
assert(status == napi_ok);
bool is_constructor = target != nullptr;
if (is_constructor) {
// Invoked as constructor: `new MyObject(...)`
size_t argc = 1;
napi_value args[1];
napi_value jsthis;
status = napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr);
assert(status == napi_ok);
double value = 0;
napi_valuetype valuetype;
status = napi_typeof(env, args[0], &valuetype);
assert(status == napi_ok);
if (valuetype != napi_undefined) {
status = napi_get_value_double(env, args[0], &value);
assert(status == napi_ok);
}
MyObject* obj = new MyObject(value);
obj->env_ = env;
status = napi_wrap(env,
jsthis,
reinterpret_cast<void*>(obj),
MyObject::Destructor,
nullptr, // finalize_hint
&obj->wrapper_);
assert(status == napi_ok);
return jsthis;
} else {
// Invoked as plain function `MyObject(...)`, turn into construct call.
size_t argc_ = 1;
napi_value args[1];
status = napi_get_cb_info(env, info, &argc_, args, nullptr, nullptr);
assert(status == napi_ok);
const size_t argc = 1;
napi_value argv[argc] = {args[0]};
napi_value cons;
status = napi_get_reference_value(env, constructor, &cons);
assert(status == napi_ok);
napi_value instance;
status = napi_new_instance(env, cons, argc, argv, &instance);
assert(status == napi_ok);
return instance;
}
}
napi_value MyObject::GetValue(napi_env env, napi_callback_info info) {
napi_status status;
napi_value jsthis;
status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);
assert(status == napi_ok);
MyObject* obj;
status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));
assert(status == napi_ok);
napi_value num;
status = napi_create_double(env, obj->value_, &num);
assert(status == napi_ok);
return num;
}
napi_value MyObject::SetValue(napi_env env, napi_callback_info info) {
napi_status status;
size_t argc = 1;
napi_value value;
napi_value jsthis;
status = napi_get_cb_info(env, info, &argc, &value, &jsthis, nullptr);
assert(status == napi_ok);
MyObject* obj;
status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));
assert(status == napi_ok);
status = napi_get_value_double(env, value, &obj->value_);
assert(status == napi_ok);
return nullptr;
}
napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) {
napi_status status;
napi_value jsthis;
status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);
assert(status == napi_ok);
MyObject* obj;
status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));
assert(status == napi_ok);
obj->value_ += 1;
napi_value num;
status = napi_create_double(env, obj->value_, &num);
assert(status == napi_ok);
return num;
}
napi_value MyObject::Multiply(napi_env env, napi_callback_info info) {
napi_status status;
size_t argc = 1;
napi_value args[1];
napi_value jsthis;
status = napi_get_cb_info(env, info, &argc, args, &jsthis, nullptr);
assert(status == napi_ok);
napi_valuetype valuetype;
status = napi_typeof(env, args[0], &valuetype);
assert(status == napi_ok);
double multiple = 1;
if (valuetype != napi_undefined) {
status = napi_get_value_double(env, args[0], &multiple);
assert(status == napi_ok);
}
MyObject* obj;
status = napi_unwrap(env, jsthis, reinterpret_cast<void**>(&obj));
assert(status == napi_ok);
napi_value cons;
status = napi_get_reference_value(env, constructor, &cons);
assert(status == napi_ok);
const int kArgCount = 1;
napi_value argv[kArgCount];
status = napi_create_double(env, obj->value_ * multiple, argv);
assert(status == napi_ok);
napi_value instance;
status = napi_new_instance(env, cons, kArgCount, argv, &instance);
assert(status == napi_ok);
return instance;
}
#ifndef TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_
#define TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_
#include <node_api.h>
class MyObject {
public:
static napi_value Init(napi_env env, napi_value exports);
static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);
private:
explicit MyObject(double value_ = 0);
~MyObject();
static napi_value New(napi_env env, napi_callback_info info);
static napi_value GetValue(napi_env env, napi_callback_info info);
static napi_value SetValue(napi_env env, napi_callback_info info);
static napi_value PlusOne(napi_env env, napi_callback_info info);
static napi_value Multiply(napi_env env, napi_callback_info info);
static napi_ref constructor;
double value_;
napi_env env_;
napi_ref wrapper_;
};
#endif // TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_
var addon = require('bindings')('addon');
var obj = new addon.MyObject(10);
console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13
console.log( obj.multiply().value ); // 13
console.log( obj.multiply(10).value ); // 130
var newobj = obj.multiply(-1);
console.log( newobj.value ); // -13
console.log( obj === newobj ); // false