{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# pybind11: Use C++ libraries\n", "\n", "\n", "[pybind11](https://pybind11.readthedocs.io) lets you write Python extensions using pure C++; no special tool or processing step needed. It's just a header-only library that works just about everywhere. Used by SciPy, PyTorch, and many more libraries." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example\n", "\n", "A Python extension in pybind11 looks like this:\n", "\n", "---\n", "\n", "```cpp\n", "#include \n", "\n", "namespace py = pybind11;\n", "\n", "int square(int x) {\n", " return x * x;\n", "}\n", "\n", "PYBIND11_MODULE(somecode, m) {\n", " m.def(\"square\", &square);\n", "}\n", "```\n", "\n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can use `cppimport` to import it for a quick test, or a build system like scikit-build-core to build. I'm not including a compiler in this environment, so I'm not going to build here - see one of my other classes. This is a minimal `CMakeLists.txt`:\n", "\n", "---\n", "\n", "```cmake\n", "cmake_minimium_required(VERSION 3.15...3.26)\n", "project(python_example LANGUAGES CXX)\n", "\n", "set(PYBIND11_FINDPYTHON ON)\n", "find_package(pybind11 CONFIG REQUIRED)\n", "\n", "pybind11_add_module(python_example MODULE src/main.cpp)\n", "\n", "install(TARGETS python_example DESTINATION .)\n", "```\n", "\n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And, your pyproject.toml:\n", "\n", "\n", "---\n", "\n", "```toml\n", "[build-system]\n", "requires = [\"scikit-build-core\", \"pybind11\"]\n", "build-backend = \"scikit_build_core.build\"\n", "\n", "[project]\n", "name = \"example\"\n", "version = \"0.0.1\"\n", "requires-python = \">=3.8\"\n", "```\n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want to build and distribute, use [cibuildwheel](https://cibuildwheel.readthedocs.io), which is used by Scikit-Learn, Matplotlib, MyPy, and many more; it can be setup for Linux, macOS, and Windows and all common CPython and PyPy versions in just 13 lines:\n", "\n", "---\n", "\n", "```yaml\n", "on: [push, pull_request]\n", "\n", "jobs:\n", " build_wheels:\n", " strategy:\n", " matrix:\n", " os: [ubuntu-latest, windows-latest, macos-latest]\n", " runs-on: ${{ matrix.os }}\n", "\n", " steps:\n", " - uses: actions/checkout@v3\n", " \n", " - uses: pypa/cibuildwheel@v2.14\n", "\n", " - uses: actions/upload-artifact@v3\n", " with:\n", " path: ./wheelhouse/*.whl\n", "```\n", "---\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## So much more\n", "\n", "Some examples of classes:\n", "\n", "```python\n", "#include \n", "\n", "using namespace pybind11::literals;\n", "\n", "PYBIND11_MODULE(example, m) {\n", " py::class_(m, \"Vector\")\n", " .def(py::init())\n", " .def_property(\"x\", &Vector::getX, &Vector::setX)\n", " .def_property(\"y\", &Vector::getY, &Vector::setY)\n", " .def(\"mag\", &Vector::mag, \"I am a mag function\")\n", " \n", " .def(\"unit\", [](const Vector& self){return self.unit();})\n", " \n", " .def(\"__str__\", [](const Vector& self){return py::str(\"[{}, {}]\").format(self.getX(), self.getY());})\n", " \n", " .def(py::self *= float())\n", " .def(float() * py::self)\n", " .def(py::self * float())\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can use lambda functions almost anywhere, and you can ask for `py::object` or the C++ type interchangeably, or cast between them. " ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.11" } }, "nbformat": 4, "nbformat_minor": 4 }