SkillAgentSearch skills...

Nobind

`pybind11`-like API for Node.js using C++17 fold expressions

Install / Use

/learn @mmomtchev/Nobind
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

nobind17

CI QA npm version License: ISC

Experimental next-gen binding framework for Node.js / Node-API inspired by pybind11

Inspired by pybind11 and embind, in turn inspired by the groundbreaking Boost.Python.

This framework is designed around C++17 fold expressions.

It has one defining characteristic that sets it apart from pybind11 and embind - every wrapper is statically generated at compile time and has no run-time state. All the state information is constexpr and it is encoded in the template parameters. The wrappers are instantiated by obtaining a pointer to the wrapper function.

This allows for both a (slightly) better performance and code simplicity.

The unit tests run on:

  • g++ 9.4 on Linux (the default compiler on Ubuntu 20.04)
  • clang 13 on macOS (the default compiler on macOS 11)
  • MSVC 19.29 on Windows (Visual Studio 16.11 aka 2019)

However because of edge cases when it comes to C++17 support, the recommended compiler versions are:

  • g++ 10.5 on Linux (the alternative choice on Ubuntu 20.04 and the default one on Ubuntu 22.04)
  • clang 13 on macOS (the default compiler on macOS 11)
  • MSVC 19.37 on Windows (Visual Studio 17.7 aka 2022)

It is meant as an easy to use entry-level light-weight binding framework for simple projects that target only Node.js.

Complex projects should continue to use SWIG which is cross-platform and cross-language.

Currently, the project should be considered of a recent release quality.

The first npm module to use it is @mmomtchev/ffmpeg, you can check it for advanced usage examples.

A future compatible layer should allow to target both embind and nobind17 with shared declarations.

Full pybind11 compatibility is also a very long term goal - allowing a module to support both Node.js and Python.

You can use nobind-example-project and hadron-nobind-example-project as a template for creating a new nobind17 based project using node-gyp or hadron as build system.

Comparison vs SWIG Node-API

| Feature | SWIG Node-API | nobind17 | | --- | --- | --- | | Design goal | Create bindings for (almost) any C or C++ calling semantic with (almost) native feel | Easy to use, easy to learn, be able to wrap most C++ calling semantics, including asynchronous methods, without understanding the Node.js memory management or thread model, be able to go a little further by tweaking the typemaps | | Target use | Commercial-grade bindings for large C++ libraries | Very fast porting of C++ code with few methods/classes | | Method of operation | Custom C++ header compiler, uses its own interface language, generates C++ code | Collection of C++ templates to be included in the project | | Method of using | Must write metaprogramming code | Must enumerate the binded methods using C++ syntax | | C++ requirements | C++11 | C++17 with some features such as wrapping of lambdas requiring C++20 | | C++ types | Almost all, nested classes support is very limited | No functions pointers, no nested classes, enums are not automatic | | C++ preprocessing integration | Yes, can expose macros to JS | No | | C++ namespaces | Can be exposed to JS with some limitations and manual work | Supported in C++ but not exposed to JS | | C++ iterators | manual | automatic | | Buffers / ArrayBuffers / TypedArrays | Yes | Only Buffers for now | | STL | Complete, supports both JS using C++ STLs without copying and C++ using JS types with copying | Limited, all passing of STL arguments is by copying | | Async | Automatic | Automatic | | Async locking | Yes, with automatic dead-lock prevention | Yes, but no deadlock prevention | | Smart pointers | Yes | Not at the moment, but planned | | TypeScript support | Automatic | Automatic | | ES6 named exports for all C/C++ functions | Yes, automatic | No, must write it | | WASM/Browser support | Yes | Not at the moment, but planned through embind compatibility | | Cross-platform | Yes | Yes | | Input language | Both C and C++ | Mostly C++, many usual C API semantics are not well supported | | Target language | Most dynamic languages | An eventual abstraction layer between nobind17, embind and pybind11 is planned in theory | | Exposing C++ inheritance to JavaScript | Yes, automatic with implicit downcasting support, diamond inheritance is not supported | Yes, but no automatic downcasting support and no diamond inheritance | | Overloading | Yes | Only for constructors, overloaded methods must be renamed to be usable in JS | | Optional arguments with default values | Yes, automatic | No, all arguments become mandatory | | Complex argument transformations (for example C++ expects (char**, size_t*) as input argument, JS expects Buffer as returned type) | Yes | Only 1:1 and 1:0 transformations of input arguments | | Custom type casters | Yes | Yes | | Interfacing between multiple modules | Yes | No |

Usage

nobind17 is a set of C++17 templates that must be included directly in the user project.

It is published as an npm package that will also install node-addon-api.

Starting from Node.js 18, C++17 is the default build mode for both Node.js itself and for addons. Unless you set manually NAPI_VERSION in your project, nobind17@2.0 defaults to NAPI_VERSION=8 which requires Node.js 16 or later. Older versions use NAPI_VERSION=6 which allows backward compatibility of the generated binary addon with Node.js 14 and later - even when using Node.js 18 as the build platform.

nobind17 is designed to be very easy to use - there is no learning curve at all - while allowing to deal with the most common situations that arise when creating bindings for C++ libraries to be used from Node.js.

The following tutorial should be enough to get you started with your C++ project.

You can also check node-ffmpeg as an example for a large project using nobind17.

The environment

Create a a binding.gyp, then create a package.json for your project and install nobind17:

binding.gyp

{
  'target_defaults': {
    'includes': [
      # These are the correct compiler options
      # to enable C++ exceptions with node-gyp
      'except.gypi'
    ]
  },
  'targets': [
    {
      'target_name': 'my-shiny-cpp-bindings',
      'sources': [
        # This is the file that contains your bindings
        # (from the tutorial below)
        'src/my-shiny-cpp-bindings.cc'
        # List your C++ files here
        # If you have a large library, check
        # https://github.com/mmomtchev/node-ffmpeg
        # for inspiration, it builds ffmpeg with conan
      ],
      'include_dirs': [
        '<!@(node -p "require(\'node-addon-api\').include")',
        '<!@(node -p "require(\'nobind17\').include")'
      ]
    }
  ]
}
npm init # ... answer questions
npm install nobind17
cp node_modules/node-addon-api/except.gypi .

You will be building your project with node-gyp configure build. node-gyp is usually installed globally.

C++17 is the default build mode starting from Node.js 18.x.

Module definition

Let's try to wrap a simple C++ class:

class Hello {
public:
  std::string name;
  Hello(const std::string &s) : name(s) {}
  std::string Greet(const std::string &s) {
    std::stringstream r;
    r << "hello " << s << " " << name_;
    return r.str();
  }
};

Start by creating a module:

// nobind will automatically include napi.h and it will
// define NAPI_VERSION and NAPI_EXPERIMENTAL
// If you include it yourself without these, you will be
// missing the synchronous finalizers
#include <nobind.h>

// Define a new module
NOBIND_MODULE(my_cpp_bindings, m) {
  // Expose a C++ class called Hello
  m.def<Hello>("Hello")
    // Include a constructor with a single const std::string & argument
    .cons<const std::string &>();
}

Adding methods

nobind17 supports global methods and instance and static class methods. All of them are declared by using .def():

// Expose a global function global_fn
m.def<&global_fn>("global_fn");
m.def<MyClass>("Hello")
  .cons<std::string &>()
  // Expose a class method (whether it is static or instance)
  .def(&Hello::Greet, "greet");

nobind17 will identify the type of the class method, static methods will be available through the class itself and instance methods will be available through the object instance.

A class can have multiple constructors, including a default one (use <> for its arguments). The number of arguments on the JavaScript side determine which one will be used. If there a multiple constructors expecting the same number of arguments, they will be tried in the order of their declaration - the first one which is able to convert its arguments will win.

Overloaded methods, other than constructors, must be explicitly resolved and each signature must have a different name in JavaScript.

Arguments will be automatically converted. The C++ type of the wrapped function selects the type converter. The basic types supported out of the box are:

| JavaScript type | C++ type | | --- | --- | | number | int, short, long, unsigned, unsigned short, unsigned long, long long, unsigned long long, double, float | | string | std::string, char * | | boolean | bool | | object | `std::map<string,

View on GitHub
GitHub Stars9
CategoryDevelopment
Updated5d ago
Forks0

Languages

C++

Security Score

90/100

Audited on Mar 27, 2026

No findings