Review docs

This commit is contained in:
Luis Blanco 2019-11-12 17:06:34 +03:00
parent 31ede2853f
commit 7f4dd4f37d
4 changed files with 108 additions and 105 deletions

View File

@ -20,10 +20,13 @@ additional snippets follow the links below.
**Go to**: **Go to**:
* [include/addon-tools.hpp](doc/addon-tools.md) * [include/addon-tools.hpp](doc/addon-tools.md)
Macro shortcuts for C++ addons using NAPI.
Macro shortcuts for C++ addons using **NAPI**.
* [Es5 Class Wrapping](doc/class-wrapping.md) * [Es5 Class Wrapping](doc/class-wrapping.md)
An alternative, lightweight native class-defining mechanism for addons. An alternative, lightweight native class-defining mechanism for addons.
* [Snippets](doc/snippets.md) * [Snippets](doc/snippets.md)
Some repetitive bits of code for addons. Some repetitive bits of code for addons.
@ -84,8 +87,8 @@ const tag = process.env.npm_package_config_install;
install(`${prefix}/${tag}`); install(`${prefix}/${tag}`);
``` ```
`prefix` - the constant base part of the download url. * `prefix` - the constant base part of the download url.
`tag` - the version-dependent part of the url, * `tag` - the version-dependent part of the url,
here `process.env.npm_package_config_install` is taken here `process.env.npm_package_config_install` is taken
([automatically](https://docs.npmjs.com/misc/config#per-package-config-settings)) ([automatically](https://docs.npmjs.com/misc/config#per-package-config-settings))
from **package.json**: from **package.json**:
@ -105,7 +108,7 @@ from **package.json**:
A [Writable](https://nodejs.org/api/stream.html#stream_writable_streams) A [Writable](https://nodejs.org/api/stream.html#stream_writable_streams)
stream buffer, that is stored in-memory and can be fully stream buffer, that is stored in-memory and can be fully
obtained when writing was finished. It is equivalent to stream-writing obtained when writing was finished. It is equivalent to stream-writing
a temporary file and then reading it into a Buffer. a temporary file and then reading it into a `Buffer`.
Use `stream.get()` to obtain the data when writing was finished: Use `stream.get()` to obtain the data when writing was finished:
``` ```

View File

@ -1,31 +1,29 @@
# include/addon-tools.hpp # include/addon-tools.hpp
There is a C++ header file, `addon-tools.hpp`, shipped with this package. It There is a C++ header file, `addon-tools.hpp`, shipped with this package. It
introduces several useful macros and utilities. Also it includes Napi automatically, introduces several useful macros and utilities. Also it includes **Napi**
so that you can replace: implicitly, so you can replace:
``` ```
#include <napi.h> #include <napi.h>
``` ```
with with
``` ```
#include <addon-tools.hpp> #include <addon-tools.hpp>
``` ```
In **GYP**, the include directory should be set for your addon.
In gyp, the include directory should be set for your addon to know where An actual path to the directory is exported from the module
to get it. An actual path to the directory is exported from the module
and is accessible like this: and is accessible like this:
``` ```
require('addon-tools-raub').include // a string require('addon-tools-raub').include // a string
``` ```
### Helpers in **addon-tools.hpp**: ### Helpers in **addon-tools.hpp**:
Usually all the helpers work within the context of JS call. In this case we Usually all the helpers work within the context of a method. In this case we
have `CallbackInfo info` passed as an argument. have `CallbackInfo info` passed as an argument. And we can return `undefined`
in case a problem has occured. So most of these macros are only usable
within `Napi::Value`-returning functions.
``` ```
#define NAPI_ENV Napi::Env env = info.Env(); #define NAPI_ENV Napi::Env env = info.Env();
@ -34,17 +32,15 @@ have `CallbackInfo info` passed as an argument.
<details> <details>
<summary>Return value</summary> <summary>**Return value**</summary>
* `RET_VALUE(VAL)`- return a given Napi::Value. * `RET_VALUE(VAL)`- return a given Napi::Value.
* `RET_UNDEFINED`- return `undefined`. * `RET_UNDEFINED`- return `undefined`.
* `RET_NULL` - return `null`. * `RET_NULL` - return `null`.
* `RET_STR(VAL)` - return `Napi::String`, expected `VAL` is `const char *`. * `RET_STR(VAL)` - return `Napi::String`, expected `VAL` is `const char *`.
* `RET_NUM(VAL)` - return `Napi::Number`, expected `VAL` is `double`. * `RET_NUM(VAL)` - return `Napi::Number`, expected `VAL` is of numeric type.
* `RET_EXT(VAL)` - return `Napi::External`, expected `VAL` is `void *`. * `RET_EXT(VAL)` - return `Napi::External`, expected `VAL` is a pointer.
* `RET_BOOL(VAL)` - return `Napi::Boolean`, expected `VAL` is `bool`. * `RET_BOOL(VAL)` - return `Napi::Boolean`, expected `VAL` is convertible to bool.
* `RET_FUN(VAL)` - return `Napi::Function`, expected `VAL` is a `napi_value`.
* `RET_OBJ(VAL)` - return `Napi::Object`, expected `VAL` is a `napi_value`.
</details> </details>
@ -52,21 +48,21 @@ have `CallbackInfo info` passed as an argument.
<details> <details>
<summary>New JS value</summary> <summary>**New JS value**</summary>
* `JS_STR(VAL)` - create a `Napi::String` value. * `JS_STR(VAL)` - create a `Napi::String`, expected `VAL` is `const char *`.
* `JS_NUM(VAL)` - create a `Napi::Number` value. * `JS_NUM(VAL)` - create a `Napi::Number`, expected `VAL` is of numeric type.
* `JS_EXT(VAL)` - create a `Napi::External` (from pointer) value. * `JS_EXT(VAL)` - create a `Napi::External`, expected `VAL` is a pointer.
* `JS_BOOL(VAL)` - create a `Napi::Boolean` value. * `JS_BOOL(VAL)` - create a `Napi::Boolean`, expected `VAL` is convertible to bool.
</details> </details>
<details> <details>
<summary>Method check</summary> <summary>**Method check**</summary>
These checks throw JS TypeError if not passed. Here `T` is always used as a typename These checks throw JS `TypeError` if not passed. `T` is always used as a typename
in error messages. `C` is a in error messages. `C` is a
[Napi::Value](https://github.com/nodejs/node-addon-api/blob/master/doc/value.md) [Napi::Value](https://github.com/nodejs/node-addon-api/blob/master/doc/value.md)
check method, like `IsObject()`. `I` is the index of argument as in `info[I]`, check method, like `IsObject()`. `I` is the index of argument as in `info[I]`,
@ -76,22 +72,21 @@ starting from `0`.
* `IS_ARG_EMPTY(I)` - check if argument `I` is `undefined` or `null` * `IS_ARG_EMPTY(I)` - check if argument `I` is `undefined` or `null`
* `CHECK_REQ_ARG(I, C, T)` - check if argument `I` is approved by `C` check. * `CHECK_REQ_ARG(I, C, T)` - check if argument `I` is approved by `C` check.
* `CHECK_LET_ARG(I, C, T)` - check if argument `I` is approved by `C` check or empty. * `CHECK_LET_ARG(I, C, T)` - check if argument `I` is approved by `C` check or empty.
* `CTOR_CHECK(T)` - check if method is called as a constructor
* `SETTER_CHECK(C, T)` - check if setter `value` is approved by `C` check. * `SETTER_CHECK(C, T)` - check if setter `value` is approved by `C` check.
* `DES_CHECK` - within dynamic method check if the instance wasn't * `DES_CHECK` - for void-returning methods, check if the instance wasn't
destroyed by `destroy()`. destroyed by `destroy()`.
* `THIS_CHECK` - check if the instance wasn't
destroyed by `destroy()`, and then fetch `env`.
</details> </details>
<details> <details>
<summary>Method arguments</summary> <summary>**Method arguments**</summary>
The idea is to ease the transition from what inside the `CallbackInfo` to Following macros convert JS arguments into C++ variables.
what you work with in C++. Three types of argument retrieval are supported:
Three types of argument retrieval are supported: `REQ_`, `USE_` and `LET_`.
The difference:
* `REQ_` - 2 params, requires an argument to have a value * `REQ_` - 2 params, requires an argument to have a value
* `USE_` - 3 params, allows the argument to be empty and have a default * `USE_` - 3 params, allows the argument to be empty and have a default
* `LET_` - 2 params, is `USE_` with a preset zero-default. * `LET_` - 2 params, is `USE_` with a preset zero-default.
@ -109,50 +104,53 @@ double x = IS_ARG_EMPTY(0) ? 0.0 : info[0].ToNumber().DoubleValue();
``` ```
That extrapolates well to all the helpers below: That extrapolates well to all the helpers below:
* `REQ_STR_ARG` - JS `string` => C++ `std::string`. | Macro | JS type | C++ type | Default |
* `USE_STR_ARG` | :--- | :---: | :---: | :---: |
* `LET_STR_ARG` - default: `""`. | `REQ_STR_ARG` | `string` | `std::string` | - |
* `REQ_INT32_ARG` - JS `number` => C++ `int32_t`. | `USE_STR_ARG` | `string` | `std::string` | - |
* `USE_INT32_ARG` | `LET_STR_ARG` | `string` | `std::string` | `""` |
* `LET_INT32_ARG` - default: `0`. | `REQ_INT32_ARG` | `number` | `int32_t` | - |
* `REQ_INT_ARG` - JS `number` => C++ `int32_t`. | `USE_INT32_ARG` | `number` | `int32_t` | - |
* `USE_INT_ARG` | `LET_INT32_ARG` | `number` | `int32_t` | `0` |
* `LET_INT_ARG` - default: `0`. | `REQ_INT_ARG` | `number` | `int32_t` | - |
* `REQ_UINT32_ARG` - JS `number` => C++ `uint32_t`. | `USE_INT_ARG` | `number` | `int32_t` | - |
* `USE_UINT32_ARG` | `LET_INT_ARG` | `number` | `int32_t` | `0` |
* `LET_UINT32_ARG` - default: `0`. | `REQ_UINT32_ARG` | `number` | `uint32_t` | - |
* `REQ_UINT_ARG` - JS `number` => C++ `uint32_t`. | `USE_UINT32_ARG` | `number` | `uint32_t` | - |
* `USE_UINT_ARG` | `LET_UINT32_ARG` | `number` | `uint32_t` | `0` |
* `LET_UINT_ARG` - default: `0`. | `REQ_UINT_ARG` | `number` | `uint32_t` | - |
* `REQ_BOOL_ARG` - JS `Boolean` => C++ `bool`. | `USE_UINT_ARG` | `number` | `uint32_t` | - |
* `USE_BOOL_ARG` | `LET_UINT_ARG` | `number` | `uint32_t` | `0` |
* `LET_BOOL_ARG` - default: `false`. | `REQ_BOOL_ARG` | `Boolean` | `bool` | - |
* `REQ_OFFS_ARG` - JS `number` => C++ `size_t`. | `USE_BOOL_ARG` | `Boolean` | `bool` | - |
* `USE_OFFS_ARG` | `LET_BOOL_ARG` | `Boolean` | `bool` | `false` |
* `LET_OFFS_ARG` - default: `0`. | `REQ_OFFS_ARG` | `number` | `size_t` | - |
* `REQ_DOUBLE_ARG` - JS `number` => C++ `double`. | `USE_OFFS_ARG` | `number` | `size_t` | - |
* `USE_DOUBLE_ARG` | `LET_OFFS_ARG` | `number` | `size_t` | `0` |
* `LET_DOUBLE_ARG` - default: `0.0`. | `REQ_DOUBLE_ARG` | `number` | `double` | - |
* `REQ_FLOAT_ARG` - JS `number` => C++ `float`. | `USE_DOUBLE_ARG` | `number` | `double` | - |
* `USE_FLOAT_ARG` | `LET_DOUBLE_ARG` | `number` | `double` | `0.0` |
* `LET_FLOAT_ARG` - default: `0.f`. | `REQ_FLOAT_ARG` | `number` | `float` | - |
* `REQ_EXT_ARG` - JS `native` => C++ `void*`. | `USE_FLOAT_ARG` | `number` | `float` | - |
* `USE_EXT_ARG` | `LET_FLOAT_ARG` | `number` | `float` | `0.f` |
* `LET_EXT_ARG` - default: `nullptr`. | `REQ_EXT_ARG` | `native` | `void*` | - |
* `REQ_FUN_ARG` - JS `function` => C++ `Napi::Function`. | `USE_EXT_ARG` | `native` | `void*` | - |
* `REQ_OBJ_ARG` - JS `object` => C++ `Napi::Object`. | `LET_EXT_ARG` | `native` | `void*` | `nullptr` |
* `USE_OBJ_ARG` | `REQ_FUN_ARG` | `function` | `Napi::Function` | - |
* `LET_OBJ_ARG` - default: `{}`. | `REQ_OBJ_ARG` | `object` | `Napi::Object` | - |
* `REQ_ARRV_ARG` - JS `ArrayBuffer` => C++ `Napi::ArrayBuffer`. | `USE_OBJ_ARG` | `object` | `Napi::Object` | - |
* `REQ_BUF_ARG` - JS `Buffer` => C++ `Napi::Buffer<uint8_t>`. | `LET_OBJ_ARG` | `object` | `Napi::Object` | `{}` |
| `REQ_ARRV_ARG` | `ArrayBuffer` | `Napi::ArrayBuffer` | - |
| `REQ_BUF_ARG` | `Buffer` | `Napi::Buffer<uint8_t>` | - |
``` ```
NAN_METHOD(test) { JS_METHOD(test) {
REQ_UINT32_ARG(0, width); REQ_UINT32_ARG(0, width); // uint32_t width
REQ_UINT32_ARG(1, height); REQ_UINT32_ARG(1, height); // uint32_t height
LET_FLOAT_ARG(2, z); LET_FLOAT_ARG(2, z); // float z
// Variables created: unsigned int width, height; float z; // An error is thrown if width or height are not passed as numbers.
// Argument z can be undefined, null, or number; error otherwise.
... ...
``` ```
@ -161,7 +159,7 @@ NAN_METHOD(test) {
<details> <details>
<summary>Setter argument</summary> <summary>**Setter argument**</summary>
Works similar to method arguments. But there is always `value` Works similar to method arguments. But there is always `value`
argument, from which a C++ value is extracted. argument, from which a C++ value is extracted.
@ -193,7 +191,7 @@ See also: [Class Wrapping](class-wrapping.md)
<details> <details>
<summary>JS Data to C++ Data</summary> <summary>**JS Data to C++ Data**</summary>
* `T *getArrayData(value, num = NULL)` - extracts TypedArray data of any type from * `T *getArrayData(value, num = NULL)` - extracts TypedArray data of any type from
the given JS value. Does not accept `Array`. Checks with `IsArrayBuffer()`. the given JS value. Does not accept `Array`. Checks with `IsArrayBuffer()`.
@ -203,7 +201,10 @@ Returns `nullptr` for empty JS values. For unacceptable values throws TypeError.
the given JS value. Checks with `IsBuffer()`. the given JS value. Checks with `IsBuffer()`.
Returns `nullptr` for empty JS values. For unacceptable values throws TypeError. Returns `nullptr` for empty JS values. For unacceptable values throws TypeError.
* `void *getData(value)` - if `value` or `value.data` is a `TypedArray|Buffer`, * `void *getData(value)` - if `value` is a `TypedArray|Buffer`,
calls `getArrayData` or `getArrayData` respectively. Returns `nullptr` in other cases. calls `getArrayData` or `getArrayData` on it. Otherwise, if
`value.data` is a `TypedArray|Buffer`,
calls `getArrayData` or `getArrayData` on it.
Returns `nullptr` in other cases.
</details> </details>

View File

@ -1,14 +1,15 @@
# Es5 class wrapping # Es5 class wrapping
For NAPI addons this allows to call `super()` from C++ and makes the class This wrapping implementation diverges from standard ES6 style wrapping.
constructor callable with `ClassName.call(obj, ...args)`. It also uses composition rather than inheritance, so it is easily pluggable.
Also multiple C++ objects can be attached to a single JS object
* For **NAPI** addons, `super()` can be called from C++ side.
* Constructor is callable with `ClassName.call(obj, ...args)`.
* Multiple C++ objects can be attached to a single JS object
if it is necessary in an inheritance scenario. if it is necessary in an inheritance scenario.
On JS side `util.inherits` * On JS side `util.inherits`
[is used](https://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor), [is used](https://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor),
and on C++ side `inheritEs5`. and on C++ side there is `inheritEs5` function.
This implementation is using composition rather than inheritance, so
it is easily pluggable.
## Class Declaration ## Class Declaration
@ -34,17 +35,15 @@ public:
}; };
``` ```
`DECLARE_ES5_CLASS` - adds utility declarations, the first argument * `DECLARE_ES5_CLASS` - adds utility declarations, the first argument
must be this class name, and the second argument will become the must be this class name, and the second argument will become the
name (arbitrary) of this function (constructor) in JS. name (arbitrary) of this function (constructor) in JS.
* `init` - can be used to initialize this class and export it.
`init` - can be used to initialize this class and export it. * `JS_DECLARE_METHOD` - declares a method, the first argument is this class,
`JS_DECLARE_METHOD` - declares a method, the first argument is this class,
the second is the name of the method to be created. the second is the name of the method to be created.
`JS_DECLARE_GETTER` - declares a getter, the first argument is this class, * `JS_DECLARE_GETTER` - declares a getter, the first argument is this class,
the second is the name of the getter to be created. the second is the name of the getter to be created.
`JS_DECLARE_SETTER` - declares a setter, the first argument is this class, * `JS_DECLARE_SETTER` - declares a setter, the first argument is this class,
the second is the name of the setter to be created. the second is the name of the setter to be created.
@ -95,14 +94,14 @@ JS_IMPLEMENT_GETTER(ClassName, isDestroyed) { THIS_CHECK;
``` ```
`IMPLEMENT_ES5_CLASS` - implements some utility functions for class wrapping. * `IMPLEMENT_ES5_CLASS` - implements some utility functions for class wrapping.
`JS_ASSIGN_METHOD` - in `init()`, assigns the given method to this class. * `JS_ASSIGN_METHOD` - in `init()`, assigns the given method to this class.
`JS_ASSIGN_GETTER` - in `init()`, assigns the given getter to this class. * `JS_ASSIGN_GETTER` - in `init()`, assigns the given getter to this class.
`JS_ASSIGN_SETTER` - in `init()`, assigns both getter and setter to this class. * `JS_ASSIGN_SETTER` - in `init()`, assigns both getter and setter to this class.
It also takes only one argument because both have the same name. It also takes only one argument because both have the same name.
`JS_IMPLEMENT_METHOD` - implements a method, the first argument is this class, * `JS_IMPLEMENT_METHOD` - implements a method, the first argument is this class,
the second is the name of the method being implemented. the second is the name of the method being implemented.
`JS_IMPLEMENT_GETTER` - implements a getter, the first argument is this class, * `JS_IMPLEMENT_GETTER` - implements a getter, the first argument is this class,
the second is the name of the getter being implemented. the second is the name of the getter being implemented.
`JS_IMPLEMENT_SETTER` - implements a setter, the first argument is this class, * `JS_IMPLEMENT_SETTER` - implements a setter, the first argument is this class,
the second is the name of the setter being implemented. the second is the name of the setter being implemented.

View File

@ -2,7 +2,7 @@
## C++ Addon building ## C++ Addon building
N-API addons are built separately from the installation, so we can't/shouldn't **NAPI** addons are built separately from the installation, so we shouldn't
put the file **binding.gyp** to the module root anymore. It is better to have a put the file **binding.gyp** to the module root anymore. It is better to have a
separate folder with a separate **package.json**, **binding.gyp** and the sources. separate folder with a separate **package.json**, **binding.gyp** and the sources.
@ -114,7 +114,7 @@ dependency include path(s).
<details> <details>
<summary>See a snipped for src/binding.gyp here</summary> <summary>**See a snipped for src/binding.gyp here**</summary>
* Assume `DEPS` is the name of an Addon Tools compliant dependency module. * Assume `DEPS` is the name of an Addon Tools compliant dependency module.
* Assume `ADDON` is the name of this addon's resulting binary. * Assume `ADDON` is the name of this addon's resulting binary.