Coming from C++ I would implement my C++ library (classes, structs, functions) in pure C++ and as a second step I would use pybind11
to wrap them so that they can be used in Python. For example:
// Vector3.hpp
struct Vector3 {
Vector3(int x, int y, int z)
: x(x), y(y), z(z) { }
double length() const {
return std::sqrt(x * x + y * y + z * z);
}
int x, y, z;
};
// PyBindings.cpp
#include <pybind11.h>
#include <Vector3.hpp>
namespace py = pybind11;
PYBIND11_MODULE(PyBindSample, m) {
py::class_<Vector3>(m, "Vector3")
.def(py::init<int, int, int>(), py::arg("x"), py::arg("y"), py::arg("z"))
.def("length", &Vector3::length)
.def_readwrite("x", &Vector3::x)
.def_readwrite("y", &Vector3::y)
.def_readwrite("z", &Vector3::z);
}
Pybind's .def
essentially points to a C++ class or function, meaning the underlying C++ library can exist without ever knowing about the Python bindings.
Looking at the blog post Calling Rust from Python using PyO3 it seems the C++ code above would look like this in Rust:
#[pyclass]
pub struct Vector3 {
#[pyo3(get, set)]
pub x: i32,
#[pyo3(get, set)]
pub y: i32,
#[pyo3(get, set)]
pub z: i32
}
#[pymethods]
impl Vector3 {
#[new]
pub fn new(x: i32, y: i32, z: i32) -> Vector3 {
Vector3 { x, y, z }
}
pub fn length(&self) -> f64 {
((self.x*self.x + self.y*self.y + self.z*self.z) as f64).sqrt()
}
}
#[pymodule]
fn rust(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<Vector3>()?;
Ok(())
}
Here I had to insert multiple PyO3 annotations like #[pyo3(get, set)]
into my Rust library. Is it possible to wrap an existing Rust library into Python without having to change any code within the underlying Rust library, similar to pybind's approach in C++ (where library & bindings are separated)? Perhaps PyO3 is not the right tool? If not, are there other tools that mimic pybind's approach?