pybind11: C++11 to Python bindings without Boost
Seamless operability between C++11 and Python
At a glance
- What is it?
- pybind11 is a header-only C++11 library that exposes C++ types and functions to Python. It replaces the Boost.Python dependency with roughly 4K lines of core headers, at the cost of a compiler and a build system you have to configure yourself.
- Who is it for?
- Adopt pybind11 when you already have a C++ codebase and a build system, and you want Python callers to use it without writing a CPython extension by hand. Skip it if you need pure Python packaging with no compiler in the loop, or if you are binding a single function and ctypes or Cython would do.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem pybind11 solves for C++ and Python teams
Writing a CPython extension by hand means touching the C API directly: reference counting, PyObject conversions, error handling, and a module init function. It is mechanical work that grows with every function you expose. pybind11 targets that work specifically. The README describes it as a lightweight header-only library that exposes C++ types in Python and vice versa, mainly to create Python bindings of existing C++ code, and says the goal is to minimize boilerplate by inferring type information using compile-time introspection.
The intended user is someone with a C++ library that already compiles, who wants Python callers to import it as a normal module. The README's own framing is historical: Boost.Python did this first, and pybind11 exists because Boost is a large dependency whose compatibility with old compilers costs arcane template tricks. Now that C++11 compilers are widely available, the README argues that machinery is an excessively large and unnecessary dependency. That is the whole pitch. It is not a new language or a code generator; it is a header set plus a compile-time type system.
If your C++ is header-only or already builds with CMake, the integration cost is low. If your code is C-style with manual memory management and no classes, the value drops, because much of what pybind11 maps (inheritance, smart pointers, STL containers, virtual methods) is exactly the C++ surface you would not be using.
How pybind11 works: compile-time introspection, not a generator
The mechanism is template metaprogramming. You write a module definition in C++ using the PYBIND11_MODULE macro, register functions and classes with m.def and py::class_, and the library infers argument and return types at compile time. The README attributes this to C++11 features specifically: tuples, lambda functions and variadic templates. Function signatures are precomputed at compile time using constexpr, which the README says leads to smaller binaries.
There is no intermediate schema file and no separate binding generator step. The bindings are ordinary C++ translation units that include pybind11 headers and are compiled into a shared object that Python imports. That is the data flow: C++ compiler produces an extension module, CPython loads it, and calls cross the boundary through generated trampolines. Types convert on the way in and out; the README notes that C++11 move constructors and move assignment operators are used whenever possible to transfer custom data types efficiently.
The feature list is broad and worth reading against your own API: functions taking and returning custom data structures by value, reference or pointer; instance and static methods; overloaded functions; attributes; arbitrary exception types; enumerations; callbacks; iterators and ranges; custom operators; single and multiple inheritance; STL data structures; smart pointers with reference counting like std::shared_ptr; and classes with virtual or pure virtual methods extended in Python. NumPy support is integrated, with the caveat that NumPy 2 requires pybind11 2.12 or later. There is also a buffer protocol path for exposing internal storage, which the README presents as the way to move between C++ matrix classes like Eigen and NumPy without expensive copies, plus automatic vectorization over NumPy array arguments.
Installing pybind11 with pip and building a first module
The repository ships a Python package, so the simplest route is pip. The pyproject.toml declares the build backend as scikit-build-core and requires Python 3.9 or newer, and the README points to three worked examples: a setuptools example, a scikit-build example and a CMake example, all under the pybind/pybind GitHub organization. The pyproject.toml also defines pybind11-config as a console script entry point, which is how build tooling locates the include directories.
pip install pybind11For the binding code itself, the README points to the tutorial and reference documentation at pybind11.readthedocs.io rather than reproducing a module definition inline, so the exact source for a first module belongs to that documentation. What the README does state about the shape of a module is the macro name, PYBIND11_MODULE, and the registration calls m.def and py::class_ used inside it. The repository also ships a CMakeLists.txt and CMakePresets.json at the top level, and the README links a dedicated CMake example project under the pybind organization for the build side.
Expect the first build to be the slow part. The header-only design moves cost from link time to compile time, and every translation unit that includes the headers pays it. The README notes a reported conversion of PyRosetta from Boost.Python with a 5.4x binary size reduction and 5.8x compile time reduction, but that is a single reported case from a linked presentation, not a general guarantee.
Where pybind11 is the wrong tool
The header-only model has a real cost that the README does not dwell on. Because type information is inferred by templates, error messages from a misregistered signature are C++ template diagnostics, and they are long. Nothing in the README suggests pybind11 validates your bindings at runtime before the module is imported; a mismatch surfaces when the compiler or the loader objects.
Compile time is the other constraint. Every file that includes the headers instantiates the machinery it uses. Projects with many binding translation units often split them, but that is a build-engineering decision the library does not make for you.
The platform story is deliberately open-ended. The README says pybind11 is exercised in CI across operating systems, Python versions, C++ standards and toolchains, but points to the GitHub Actions and AppVeyor logs for the current combinations rather than listing them. It then says closely related versions of a tested compiler or platform will often work in practice but cannot be promised, and that the matrix balances coverage against CI resource limits such as GitHub's concurrency caps on the free tier. Read that as a boundary: if your target is an unusual compiler or an older Python, you are outside what is validated, and the README explicitly invites issues and pull requests to extend coverage rather than promising support.
Finally, if you only need to call one C function from Python, ctypes needs no compiler at all. pybind11 is for exposing a C++ API surface, not for a single call.
pybind11 vs nanobind and the Boost.Python inheritance
The README names Boost.Python as the predecessor and explains the split in approach: Boost.Python accepts almost every C++ compiler in existence, and the README says that compatibility requires arcane template tricks and workarounds for the oldest and buggiest compilers. pybind11 drops that requirement, assumes C++11, and the README describes the result as a tiny self-contained version of Boost.Python with everything not relevant to binding generation stripped away. Practically, that means pybind11 is a few headers depending only on CPython (or PyPy, or GraalPy) and the C++ standard library, whereas Boost.Python pulls in Boost.
The nanobind comparison is the one people search for, and it is a different trade-off rather than a strict upgrade. The README does not describe nanobind, so the honest statement is limited: pybind11's own README positions it as the mature, feature-complete option whose goal is broad mapping of C++ constructs, and its release history shows incremental 3.x releases (v3.0.3 in March 2026, v3.0.4 in April 2026, v3.1.0 in August 2026). Anyone weighing the two should compare the specific features they need, such as the NumPy buffer path, smart pointer handling, or Python subclassing of virtual C++ classes, against each project's documentation rather than against a general reputation.
If your existing codebase is already a Boost.Python project, the README's own example is a conversion in that direction, and the reported PyRosetta numbers are the argument for doing it. If you are starting fresh with no Boost dependency, the question is only which of the C++11-era binding libraries matches your API.
Licence, maintenance and the cost of upgrading
The repository's licence metadata reports NOASSERTION, but pyproject.toml is explicit: license = "BSD-3-Clause", with license-files = ["LICENSE"]. The README also says pybind11 is provided under a BSD-style license found in the LICENSE file. For a binding library that gets compiled into your extension, the practical question is what obligations travel with the resulting binary. A BSD-3-Clause licence is permissive, but whether and how you reproduce the notice is a decision for your own legal review, not something this article can settle.
On maintenance, the last push to the default branch was on 2026-09-18, and the repository is not archived. The most recent release is v3.1.0 from 2026-08-06. The project also carries a SECURITY.md and a contributing guide, and the README thanks Google for financial support of its CI infrastructure, which is the kind of detail that matters when you are deciding whether a dependency will still be buildable in three years.
Upgrade cost is mostly a build cost. Because pybind11 is header-only, a version bump means recompiling every translation unit that includes it, and template diagnostics can change between versions. The 3.x line is recent relative to 2.x, and the README already flags one hard compatibility edge: NumPy 2 requires pybind11 2.12 or later. If you pin an older pybind11 and your users move to NumPy 2, that combination is documented as broken.
Editorial conclusion
Adopt pybind11 when you already have a C++ codebase and a build system, and you want Python callers to use it without writing a CPython extension by hand. Skip it if you need pure Python packaging with no compiler in the loop, or if you are binding a single function and ctypes or Cython would do. Before committing, verify that your target Python version is in the project's test matrix, since the README says the matrix evolves and that closely related versions will often work but are not promised. Then check the LICENSE file itself: the repository metadata reports NOASSERTION while pyproject.toml declares BSD-3-Clause.
Frequently asked questions
What is pybind11 used for?
It creates Python bindings for existing C++ code, exposing C++ functions, classes, enums, STL containers and smart pointers as importable Python modules. The README describes the goal as minimizing boilerplate by inferring type information through compile-time introspection.
Is pybind11 header only?
Yes. The README states that everything is contained in just a few header files and that there is no need to link against any additional libraries, with the core headers requiring about 4K lines without comments.
How do I install pybind11?
Install the Python package, then let your build system locate the headers. The repository declares a scikit-build-core build backend and a pybind11-config console script, and the README links setuptools, scikit-build and CMake examples.
Which is better, pybind11 or nanobind?
The README does not describe nanobind, so this cannot be answered from the project's own documentation. What can be said is that pybind11's README frames it as a stripped-down Boost.Python replacement assuming C++11, and that the choice depends on which C++ features you need mapped.
How does pybind11 work?
It uses compile-time introspection through C++11 tuples, lambda functions and variadic templates to infer types, and precomputes function signatures with constexpr. The bindings are ordinary C++ translation units compiled into a shared object that Python imports.
Is pybind11 fast?
The README claims binaries are generally smaller by a factor of at least 2 compared to equivalent Boost.Python bindings, and cites a reported PyRosetta conversion with a 5.4x binary size reduction and 5.8x compile time reduction. That is a single reported case, not a general benchmark.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pybind-pybind11)