Seamless operability between C++11 and Python for creating Python bindings of existing C++ code
—
pybind11 provides comprehensive integration with NumPy arrays, enabling high-performance data exchange between C++ and Python numerical code. This integration supports the buffer protocol, automatic type conversion, and vectorization of C++ functions.
Core classes for working with NumPy arrays from C++.
class array : public object {
public:
// Constructors
array();
array(const buffer_info &info);
// Array properties
ssize_t ndim() const; // Number of dimensions
const ssize_t* shape() const; // Array shape
const ssize_t* strides() const; // Array strides
ssize_t size() const; // Total number of elements
ssize_t itemsize() const; // Size of each element
ssize_t nbytes() const; // Total number of bytes
// Data access
void* data() const; // Raw data pointer
template<typename T> T* data() const; // Typed data pointer
// Array metadata
std::string dtype() const; // NumPy dtype string
bool owndata() const; // Whether array owns its data
bool writeable() const; // Whether array is writeable
// Buffer protocol
buffer_info request(bool writable = false) const;
};
template<typename T>
class array_t : public array {
public:
// Type-specific array wrapper
using value_type = T;
// Constructors
array_t();
array_t(size_t size);
array_t(const std::vector<size_t> &shape);
array_t(const std::vector<size_t> &shape, const std::vector<size_t> &strides);
array_t(const buffer_info &info);
// Typed data access
T* data() const;
T* mutable_data() const;
// Element access (for 1D arrays)
T& operator[](ssize_t index);
const T& operator[](ssize_t index) const;
// Multi-dimensional access
template<typename... Indices>
T& operator()(Indices... indices);
template<typename... Indices>
const T& operator()(Indices... indices) const;
// Iteration
T* begin() const;
T* end() const;
};NumPy data type integration and conversion.
class dtype : public object {
public:
// Get dtype for C++ type
template<typename T>
static dtype of();
// Dtype properties
ssize_t itemsize() const;
std::string format() const;
char kind() const;
// Type checking
bool is_equiv(const dtype &other) const;
};
// Supported automatic conversions:
// C++ type -> NumPy dtype
// bool -> bool
// int8_t -> int8
// uint8_t -> uint8
// int16_t -> int16
// uint16_t -> uint16
// int32_t -> int32
// uint32_t -> uint32
// int64_t -> int64
// uint64_t -> uint64
// float -> float32
// double -> float64
// std::complex<float> -> complex64
// std::complex<double> -> complex128Automatically vectorize C++ functions to work with NumPy arrays.
// Vectorize a function to work element-wise on arrays
template<typename Func>
auto vectorize(Func &&f);
// Vectorize with custom function object
template<typename Return, typename... Args>
class vectorized_function {
public:
vectorized_function(std::function<Return(Args...)> f);
// Call operator for vectorized execution
array_t<Return> operator()(const array_t<Args>&... arrays);
};Direct integration with Python's buffer protocol for efficient data sharing.
class buffer_info {
public:
void *ptr; // Pointer to buffer data
ssize_t itemsize; // Size of individual items in bytes
std::string format; // Buffer format string (struct module style)
ssize_t ndim; // Number of dimensions
std::vector<ssize_t> shape; // Shape of buffer
std::vector<ssize_t> strides; // Strides for each dimension
bool readonly; // Whether buffer is read-only
buffer_info(void *ptr, ssize_t itemsize, const std::string &format,
ssize_t ndim, std::vector<ssize_t> shape,
std::vector<ssize_t> strides, bool readonly = false);
};
// Enable buffer protocol for custom classes
class MyClass {
public:
buffer_info get_buffer_info(); // Implement this method
};
// In binding code:
py::class_<MyClass>(m, "MyClass")
.def_buffer(&MyClass::get_buffer_info);Control memory allocation and lifetime for NumPy arrays.
// Memory management flags for array creation
enum class array_c_style { f = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_ };
enum class array_f_style { f = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_ };
enum class array_forcecast { f = detail::npy_api::NPY_ARRAY_FORCECAST_ };
// Create array with specific memory layout
template<typename T>
array_t<T> make_array(const std::vector<size_t> &shape,
const T* data = nullptr,
handle base = handle());Integration with the Eigen linear algebra library (requires #include <pybind11/eigen.h>).
// Automatic conversion between Eigen matrices and NumPy arrays
// Eigen::Matrix<T, Rows, Cols> <-> numpy.ndarray
// Eigen::VectorXd <-> numpy.ndarray (1D)
// Eigen::MatrixXd <-> numpy.ndarray (2D)
// Eigen types are automatically supported in function signatures
Eigen::MatrixXd process_matrix(const Eigen::MatrixXd& input);
Eigen::VectorXd solve_system(const Eigen::MatrixXd& A, const Eigen::VectorXd& b);#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
// Function that works with NumPy arrays
py::array_t<double> square_array(py::array_t<double> input) {
// Get buffer info
py::buffer_info buf = input.request();
// Check that we have a 1-D array
if (buf.ndim != 1)
throw std::runtime_error("Input array must be 1-dimensional");
// Create output array
auto result = py::array_t<double>(buf.size);
py::buffer_info res_buf = result.request();
// Perform computation
double *input_ptr = static_cast<double*>(buf.ptr);
double *output_ptr = static_cast<double*>(res_buf.ptr);
for (size_t i = 0; i < buf.shape[0]; i++) {
output_ptr[i] = input_ptr[i] * input_ptr[i];
}
return result;
}
PYBIND11_MODULE(example, m) {
m.def("square_array", &square_array, "Square all elements in array");
}#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
// Simple scalar function
double compute_value(double x, double y) {
return x * x + y * y;
}
PYBIND11_MODULE(example, m) {
// Vectorize the function automatically
m.def("compute_vectorized", py::vectorize(compute_value),
"Vectorized computation of x^2 + y^2");
}
// Python usage:
// import numpy as np
// x = np.array([1, 2, 3])
// y = np.array([4, 5, 6])
// result = compute_vectorized(x, y) # Returns array([17, 29, 45])#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
py::array_t<double> process_2d_array(py::array_t<double> input) {
py::buffer_info buf = input.request();
if (buf.ndim != 2)
throw std::runtime_error("Input array must be 2-dimensional");
int rows = buf.shape[0];
int cols = buf.shape[1];
// Create output array with same shape
auto result = py::array_t<double>({rows, cols});
py::buffer_info res_buf = result.request();
double *input_ptr = static_cast<double*>(buf.ptr);
double *output_ptr = static_cast<double*>(res_buf.ptr);
// Process each element (example: apply smoothing filter)
for (int i = 1; i < rows - 1; i++) {
for (int j = 1; j < cols - 1; j++) {
double sum = 0.0;
for (int di = -1; di <= 1; di++) {
for (int dj = -1; dj <= 1; dj++) {
sum += input_ptr[(i + di) * cols + (j + dj)];
}
}
output_ptr[i * cols + j] = sum / 9.0;
}
}
return result;
}
PYBIND11_MODULE(example, m) {
m.def("process_2d_array", &process_2d_array);
}#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <vector>
namespace py = pybind11;
class DataContainer {
std::vector<double> data_;
std::vector<size_t> shape_;
public:
DataContainer(const std::vector<size_t>& shape) : shape_(shape) {
size_t size = 1;
for (auto dim : shape) size *= dim;
data_.resize(size, 0.0);
}
// Enable buffer protocol
py::buffer_info get_buffer_info() {
return py::buffer_info(
data_.data(), // Pointer to data
sizeof(double), // Size of one scalar
py::format_descriptor<double>::format(), // Python struct-style format
shape_.size(), // Number of dimensions
shape_, // Buffer dimensions
calculate_strides(shape_) // Strides for each index
);
}
private:
std::vector<size_t> calculate_strides(const std::vector<size_t>& shape) {
std::vector<size_t> strides(shape.size());
size_t stride = sizeof(double);
for (int i = shape.size() - 1; i >= 0; i--) {
strides[i] = stride;
stride *= shape[i];
}
return strides;
}
};
PYBIND11_MODULE(example, m) {
py::class_<DataContainer>(m, "DataContainer", py::buffer_protocol())
.def(py::init<const std::vector<size_t>&>())
.def_buffer(&DataContainer::get_buffer_info);
}
// Python usage:
// container = DataContainer([10, 20])
// array = np.array(container, copy=False) # Direct access to C++ data#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <Eigen/Dense>
namespace py = pybind11;
// Automatic conversion between Eigen and NumPy
Eigen::MatrixXd matrix_multiply(const Eigen::MatrixXd& A, const Eigen::MatrixXd& B) {
return A * B;
}
Eigen::VectorXd solve_linear_system(const Eigen::MatrixXd& A, const Eigen::VectorXd& b) {
return A.colPivHouseholderQr().solve(b);
}
PYBIND11_MODULE(example, m) {
m.def("matrix_multiply", &matrix_multiply);
m.def("solve_linear_system", &solve_linear_system);
}
// Python usage:
// import numpy as np
// A = np.random.random((5, 5))
// B = np.random.random((5, 3))
// C = matrix_multiply(A, B) # Automatic conversion// Avoid unnecessary copying
py::array_t<double> process_inplace(py::array_t<double> array) {
// Request mutable buffer to modify in-place
py::buffer_info buf = array.request(/* writable = */ true);
double* ptr = static_cast<double*>(buf.ptr);
// Modify data in-place...
return array; // Return modified array
}
// Control memory layout
auto create_c_contiguous() {
return py::array_t<double>(
{100, 100}, // shape
{100 * sizeof(double), sizeof(double)} // C-style strides
);
}
auto create_f_contiguous() {
return py::array_t<double>(
{100, 100}, // shape
{sizeof(double), 100 * sizeof(double)} // Fortran-style strides
);
}namespace pybind11 {
// Core NumPy types
class array;
template<typename T> class array_t;
class dtype;
class buffer_info;
// Vectorization
template<typename Func> auto vectorize(Func &&f);
template<typename Return, typename... Args> class vectorized_function;
// Memory layout flags
enum class array_c_style;
enum class array_f_style;
enum class array_forcecast;
// Format descriptors for buffer protocol
template<typename T> struct format_descriptor;
// Buffer protocol utilities
template<typename T>
py::buffer_info get_buffer_info(T *ptr, ssize_t size);
}Install with Tessl CLI
npx tessl i tessl/pypi-pybind11