{"id":15048025,"url":"https://github.com/chokobole/node-binding","last_synced_at":"2025-04-10T01:10:56.858Z","repository":{"id":143897449,"uuid":"229578677","full_name":"chokobole/node-binding","owner":"chokobole","description":"A header only library to bind c++ to nodejs using node-addon-api easily.","archived":false,"fork":false,"pushed_at":"2022-09-16T13:20:33.000Z","size":49,"stargazers_count":20,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-10T01:10:46.728Z","etag":null,"topics":["bazel-support","cpp","cpp-library","cpp14","cpp14-library","header-only","node-addon-api","nodejs"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chokobole.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-12-22T14:16:39.000Z","updated_at":"2024-02-10T02:48:03.000Z","dependencies_parsed_at":"2023-09-04T06:30:16.417Z","dependency_job_id":null,"html_url":"https://github.com/chokobole/node-binding","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chokobole%2Fnode-binding","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chokobole%2Fnode-binding/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chokobole%2Fnode-binding/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chokobole%2Fnode-binding/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chokobole","download_url":"https://codeload.github.com/chokobole/node-binding/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248137886,"owners_count":21053775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bazel-support","cpp","cpp-library","cpp14","cpp14-library","header-only","node-addon-api","nodejs"],"created_at":"2024-09-24T21:07:08.786Z","updated_at":"2025-04-10T01:10:56.853Z","avatar_url":"https://github.com/chokobole.png","language":"C++","readme":"# Node Binding\n\nThis is a helper to bind `c++` to `nodejs` using [node-addon-api](https://github.com/nodejs/node-addon-api) easily.\n\n## Contents\n- [Node Binding](#node-binding)\n  - [Contents](#contents)\n  - [Overview](#overview)\n  - [How to use](#how-to-use)\n    - [bazel](#bazel)\n    - [node-gyp](#node-gyp)\n  - [Usages](#usages)\n    - [InstanceMethod with default arguments](#instancemethod-with-default-arguments)\n    - [Constructor](#constructor)\n    - [InstanceAccessor](#instanceaccessor)\n    - [STL containers](#stl-containers)\n    - [Conversion](#conversion)\n    - [Custom Conversion](#custom-conversion)\n\n## Overview\n\nPlease don't repeat `argument checking` or `type conversion`.\n\n```c++\n#include \"napi.h\"\n\ndouble CAdd(double arg0, double arg1) { return arg0 + arg1; }\n\nNapi::Value Add(const Napi::CallbackInfo\u0026 info) {\n  Napi::Env env = info.Env();\n  if (info.Length() \u003c 2) {\n    Napi::TypeError::New(env, \"Wrong number of arguments\").ThrowAsJavaScriptException();\n    return env.Null();\n  }\n  if (!info[0].IsNumber() || !info[1].IsNumber()) {\n    Napi::TypeError::New(env, \"Wrong arguments\").ThrowAsJavaScriptException();\n    return env.Null();\n  }\n  double arg0 = info[0].As\u003cNapi::Number\u003e().DoubleValue();\n  double arg1 = info[1].As\u003cNapi::Number\u003e().DoubleValue();\n  Napi::Number num = Napi::Number::New(env, CAdd(arg0, arg1);\n  return num;\n}\n```\n\nInstead do like below! Just this one line does everything above.\n\n```c++\n#include \"node_binding/typed_call.h\"\n\ndouble CAdd(double arg0, double arg1) { return arg0 + arg1; }\n\nNapi::Value Add(const Napi::CallbackInfo\u0026 info) {\n  return node_binding::TypedCall(info, \u0026CAdd);\n}\n```\n\n## How to use\n\n### bazel\n\nTo use `node_binding`, add the followings to your `WORKSPACE` file.\n\n```python\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\nhttp_archive(\n    name = \"node_binding\",\n    sha256 = \"\u003csha256\u003e\",\n    strip_prefix = \"node-binding-\u003ccommit\u003e\",\n    urls = [\n        \"https://github.com/chokobole/node-binding/archive/\u003ccommit\u003e.tar.gz\",\n    ],\n)\n\nload(\"@node_binding//bazel:node_binding_deps.bzl\", \"node_binding_deps\")\n\nnode_binding_deps()\n\nload(\"@node_binding//third_party/node_addon_api:install_node_addon_api.bzl\", \"install_node_addon_api\")\n\ninstall_node_addon_api(name = \"node_addon_api\")\n```\n\nThen, in your `BUILD` files, import and use the rules.\n\n```python\nload(\"@node_binding//bazel:node_binding.bzl\", \"node_binding\")\nload(\"@node_binding//bazel:node_binding_cc.bzl\", \"node_binding_copts\")\n\nnode_binding(\n    name = \"name\",\n    srcs = [\n      ...\n    ],\n    copts = node_binding_copts(),\n    deps = [\n        \"@node_binding\",\n    ],\n)\n```\n\nThen this rule generates `name.node`.\n\n### node-gyp\n\nTo use `node-binding`, install package via `npm install`.\n\n```bash\nnpm install node-binding\n```\n\nFollow [examples](https://github.com/nodejs/node-addon-examples)! But in `include_dirs`, fill like below.\n\n```python\n{\n  \"targets\": [\n    {\n      ...\n      \"include_dirs\": [\n        \"\u003c!@(node -p \\\"require('node-binding').include\\\")\",\n      ],\n    }\n  ]\n}\n```\n\n## Usages\n\n### InstanceMethod with default arguments\n\n```c++\n// examples/calculator.h\nclass Calculator {\n public:\n  void Increment(int a = 1) { result_ += a; }\n\n private:\n  int result_;\n};\n```\n\n```c++\n// examples/calculator_js.h\nclass CalculatorJs : public Napi::ObjectWrap\u003cCalculatorJs\u003e {\n public:\n  void Increment(const Napi::CallbackInfo\u0026 info);\n};\n```\n\n```c++\n// examples/calculator_js.cc\nvoid CalculatorJs::Increment(const Napi::CallbackInfo\u0026 info) {\n  if (info.Length() == 0) {\n    TypedCall(info, \u0026Calculator::Increment, calculator_.get(), 1);\n  } else {\n    TypedCall(info, \u0026Calculator::Increment, calculator_.get());\n  }\n}\n```\n\n```js\n// examples/calculator.js\nconst c = new calculator.Calculator();\nc.increment();\nc.increment(1);\n```\n\n### Constructor\n\nTo bind constructor, you have to include `#include \"node_binding/constructor.h\"`.\n\n`Constructor\u003cClass\u003e::CallNew\u003cArgs\u003e` calls `new Class(Args)`, whereas, `Constructor\u003cClass\u003e::Calls\u003cArgs\u003e` calls `Class(Args)`.\n\n```c++\n// examples/calculator.h\nclass Calculator {\n public:\n  Calculator() : result_(0) {}\n  explicit Calculator(int result) : result_(result) {}\n\n private:\n  int result_;\n};\n```\n\n```c++\n// examples/calculator_js.h\nclass CalculatorJs : public Napi::ObjectWrap\u003cCalculatorJs\u003e {\n public:\n  CalculatorJs(const Napi::CallbackInfo\u0026 info);\n};\n```\n\n```c++\n// examples/calculator_js.cc\n#include \"node_binding/constructor.h\"\n\nCalculatorJs::CalculatorJs(const Napi::CallbackInfo\u0026 info)\n    : Napi::ObjectWrap\u003cCalculatorJs\u003e(info) {\n  Napi::Env env = info.Env();\n  if (info.Length() == 0) {\n    calculator_ = std::unique_ptr\u003cCalculator\u003e(\n        TypedConstruct(info, \u0026Constructor\u003cCalculator\u003e::CallNew\u003c\u003e));\n  } else if (info.Length() == 1) {\n    calculator_ = std::unique_ptr\u003cCalculator\u003e(\n        TypedConstruct(info, \u0026Constructor\u003cCalculator\u003e::CallNew\u003cint\u003e));\n  } else {\n    THROW_JS_WRONG_NUMBER_OF_ARGUMENTS(env);\n  }\n\n  if (env.IsExceptionPending()) calculator_.reset();\n}\n```\n\n```js\n// examples/calculator.js\nnew Calculator();\nnew Calculator(0);\nnew Calculator(0, 0);  // Throws exception!\n```\n\n### InstanceAccessor\n\n```c++\n// examples/point.h\nstruct Point {\n  int x;\n  int y;\n\n  Point(int x = 0, int y = 0) : x(x), y(y) {}\n};\n```\n\n```c++\n// examples/point_js.h\nclass PointJs : public Napi::ObjectWrap\u003cPointJs\u003e {\n public:\n  void SetX(const Napi::CallbackInfo\u0026 info, const Napi::Value\u0026 v);\n  void SetY(const Napi::CallbackInfo\u0026 info, const Napi::Value\u0026 v);\n\n  Napi::Value GetX(const Napi::CallbackInfo\u0026 info);\n  Napi::Value GetY(const Napi::CallbackInfo\u0026 info);\n\n private:\n  Point point_;\n};\n```\n\n```c++\n// examples/point_js.cc\nvoid PointJs::SetX(const Napi::CallbackInfo\u0026 info, const Napi::Value\u0026 v) {\n  point_.x = ToNativeValue\u003cint\u003e(v);\n}\n\nvoid PointJs::SetY(const Napi::CallbackInfo\u0026 info, const Napi::Value\u0026 v) {\n  point_.y = ToNativeValue\u003cint\u003e(v);\n}\n\nNapi::Value PointJs::GetX(const Napi::CallbackInfo\u0026 info) {\n  return ToJSValue(info, point_.x);\n}\n\nNapi::Value PointJs::GetY(const Napi::CallbackInfo\u0026 info) {\n  return ToJSValue(info, point_.y);\n}\n\n```\n\n```js\n// examples/point.js\nconst p = new Point();\np.x = 1;\np.y = p.x * 2;\n```\n\n### STL containers\n\nTo bind `std::vector\u003cT\u003e`, you have to include `#include \"node_binding/stl.h\"`.\n\n```c++\n// test/4_stl/addon.cc\n#include \"node_binding/stl.h\"\n\nint Sum(const std::vector\u003cint\u003e\u0026 vec) {\n  int ret = 0;\n  for (int v: vec) {\n    ret += v;\n  }\n  return ret;\n}\n\nstd::vector\u003cint\u003e LinSpace(int from, int to, int step) {\n  std::vector\u003cint\u003e ret;\n  for (int i = from ;i \u003c to; i += step) {\n    ret.push_back(i);\n  }\n  return ret;\n}\n```\n\n```js\n// test/test.js\nconsole.log(sum([1, 2, 3]));  // 6\nconsole.log(linSpace(1, 5, 1));  // [1, 2, 3, 4]\n```\n\n### Conversion\n\n| c++         | js                | REFERENCE                          |\n| ----------: | ----------------: | ---------------------------------: |\n| bool        | boolean           |                                    |\n| uint8_t     | number            |                                    |\n| int8_t      | number            |                                    |\n| uint16_t    | number            |                                    |\n| int16_t     | number            |                                    |\n| uint32_t    | number            |                                    |\n| int32_t     | number            |                                    |\n| uint64_t    | number or BigInt  | BigInt if NAPI_EXPERIMENTAL is on  |\n| uint64_t    | number or BigInt  | BigInt if NAPI_EXPERIMENTAL is on  |\n| float       | number            |                                    |\n| double      | number            |                                    |\n| std::string | string            |                                    |\n| std::vector | Array             |                                    |\n\n### Custom Conversion\n\nTo bind your own class, you have to include `#include \"node_binding/type_convertor.h\"` and write your own specialized template class `TypeConvertor\u003c\u003e` for your own class.\n\n```c++\n// examples/point_js.h\n#include \"node_binding/type_convertor.h\"\n\nclass PointJs : public Napi::ObjectWrap\u003cPointJs\u003e {\n public:\n  static Napi::Object New(Napi::Env env, const Point\u0026 p);\n};\n\nnamespace node_binding {\n\ntemplate \u003c\u003e\nclass TypeConvertor\u003cPoint\u003e {\n public:\n  static Point ToNativeValue(const Napi::Value\u0026 value) {\n    Napi::Object obj = value.As\u003cNapi::Object\u003e();\n\n    return {\n        TypeConvertor\u003cint\u003e::ToNativeValue(obj[\"x\"]),\n        TypeConvertor\u003cint\u003e::ToNativeValue(obj[\"y\"]),\n    };\n  }\n\n  static bool IsConvertible(const Napi::Value\u0026 value) {\n    if (!value.IsObject()) return false;\n\n    Napi::Object obj = value.As\u003cNapi::Object\u003e();\n\n    return TypeConvertor\u003cint\u003e::IsConvertible(obj[\"x\"]) \u0026\u0026\n           TypeConvertor\u003cint\u003e::IsConvertible(obj[\"y\"]);\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo\u0026 info,\n                               const Point\u0026 value) {\n    return PointJs::New(info.Env(), value);\n  }\n};\n\n}  // namespace node_binding\n```\n\n```c++\n// examples/point_js.cc\nNapi::Object PointJs::New(Napi::Env env, const Point\u0026 p) {\n  Napi::EscapableHandleScope scope(env);\n\n  Napi::Object object = constructor_.New({\n      Napi::Number::New(env, p.x),\n      Napi::Number::New(env, p.y),\n  });\n\n  return scope.Escape(napi_value(object)).ToObject();\n}\n```\n\n```c++\n// examples/rect.h\n#include \"examples/point.h\"\n\nstruct Rect {\n  Point top_left;\n  Point bottom_right;\n};\n```\n\n```c++\n// examples/rect_js.h\nclass RectJs : public Napi::ObjectWrap\u003cRectJs\u003e {\n public:\n  void SetTopLeft(const Napi::CallbackInfo\u0026 info, const Napi::Value\u0026 v);\n  void SetBottomRight(const Napi::CallbackInfo\u0026 info, const Napi::Value\u0026 v);\n\n  Napi::Value GetTopLeft(const Napi::CallbackInfo\u0026 info);\n  Napi::Value GetBottomRight(const Napi::CallbackInfo\u0026 info);\n};\n```\n\n```c++\n// examples/rect_js.cc\n#include \"examples/point_js.h\"\n\nvoid RectJs::SetTopLeft(const Napi::CallbackInfo\u0026 info, const Napi::Value\u0026 v) {\n  if (IsConvertible\u003cPoint\u003e(v)) {\n    rect_.top_left = ToNativeValue\u003cPoint\u003e(v);\n  }\n}\n\nvoid RectJs::SetBottomRight(const Napi::CallbackInfo\u0026 info,\n                            const Napi::Value\u0026 v) {\n  if (IsConvertible\u003cPoint\u003e(v)) {\n    rect_.bottom_right = ToNativeValue\u003cPoint\u003e(v);\n  }\n}\n\nNapi::Value RectJs::GetTopLeft(const Napi::CallbackInfo\u0026 info) {\n  return ToJSValue(info, rect_.top_left);\n}\n\nNapi::Value RectJs::GetBottomRight(const Napi::CallbackInfo\u0026 info) {\n  return ToJSValue(info, rect_.bottom_right);\n}\n```\n\n```js\n// examples/rect.js\nconst topLeft = new Point(1, 5);\nconst bottomRight = new Point(5, 1);\nconst rect = new Rect(topLeft, bottomRight);\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchokobole%2Fnode-binding","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchokobole%2Fnode-binding","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchokobole%2Fnode-binding/lists"}