Content
This module relies on:
- https://github.com/leskovec/pyC_intro
The central idea is that a pybind11 project contains a small binding file that tells Python what C++ functions (and later, classes) it can call.
A minimal pybind11 binding file
Here is a very simple pybind11 binding file. Compiling it produces a shared library that Python
can import as a module (here: hello).
#include <pybind11/pybind11.h>
#include "hello.h"
PYBIND11_MODULE(hello, m) {
m.doc() = "pybind11 hello plugin";
m.def("say_hello", &say_hello, "A function which says hello");
}
Let’s unpack this line by line, because this is the whole “bridge” between C++ and Python.
1) The pybind11 header
#include <pybind11/pybind11.h>
This is the core pybind11 interface. It provides:
- the
PYBIND11_MODULE(...)macro (the module’s import entry point) - the C++ types used to represent Python objects (like the module object
m) - the registration helpers like
m.def(...)
Conceptually: this header gives you the tools to construct a Python module from C++.
2) Your C++ code stays normal C++
#include "hello.h"
This is an underappreciated point: the function you expose (here say_hello) lives in your own header
and can remain ordinary C++ code. In real research code, the computational routines usually already exist.
pybind11 is just the thin layer that exposes selected routines to Python.
3) The module entry point
PYBIND11_MODULE(hello, m) { ... }
This macro defines the initialization function that Python calls when it runs:
import hello
hellois the Python module namemis the module object you populate with functions, classes, constants, etc.
Read it as: “When Python imports hello, create the module m and register things into it.”
4) m.doc() sets the module docstring
m.doc() = "pybind11 hello plugin";
Python modules have docstrings, just like functions. Setting m.doc() makes interactive use nicer:
import hello
print(hello.__doc__)
help(hello)
In a workshop, this is also a sanity check that you imported the module you think you imported.
5) m.def(...) exposes a C++ function to Python
m.def("say_hello", &say_hello, "A function which says hello");
This line registers a Python-visible function:
"say_hello"is the name Python users will call:hello.say_hello()&say_hellois a pointer to the C++ function- the last string is the function docstring shown by
help(hello.say_hello)
When Python calls hello.say_hello(), pybind11 converts Python arguments (if any) into C++ values,
calls the C++ function, and converts the return value back into a Python object.
What this example is really teaching
Even though this is tiny, it contains the full pattern you’ll reuse throughout the workshop:
- Your “real” C++ code lives in your own headers/sources.
- The binding file includes those headers.
- Everything Python can see is registered inside
PYBIND11_MODULE.
Once this is clear, the rest becomes less mystical: you’re writing “registration code” and choosing a good Python-facing API.