Reputation: 3245
Say I have the following C++ Date
class. Using pybind11
I can easily wrap it to be used in Python:
/* date.hpp */
class Date
{
public:
int year, month, day;
Date(int year, int month, int day);
Date add_days(int N) const; // Returns new date with N days added
bool is_weekend() const; // Returns true if date is Sat or Sun
};
/* date_bindings.cpp */
#include <pybind11/pybind11.h>
#include <date.hpp>
namespace py = pybind11;
PYBIND11_MODULE(date_py, m) {
py::class_<Date>(m, "Date")
.def(py::init<int, int, int>(), py::arg("year"), py::arg("month"), py::arg("day"))
.def("add_days", &Date::add_days, py::arg("N"))
.def("is_weekend", &Date::is_weekend)
.def_readwrite("year", &Date::year)
.def_readwrite("month", &Date::month)
.def_readwrite("day", &Date::day);
}
Now, I want to do the same in Rust. So I implemented my Date
class and then used PyO3
to wrap it. See code below.
I've noticed that the PyO3
bindings contain a lot of boilerplate, and hence they are much longer compared to pybind11
:
Am I using PyO3 correctly? Are there ways to shorten the code to achieve a similar level of "conciseness" as is the case for pybind11?
edit: If there is a way to create bindings without the DatePy
helper struct, I would actually prefer that.
/* date.rs */
#[derive(Debug)]
pub struct Date {
pub year: u16,
pub month: u8,
pub day: u8
}
impl Date {
pub const fn add_days(&self, N: u8) -> Self {
// Simplified logic for demonstration purposes
Date{year: self.year, month: self.month, day: self.day + N}
}
pub const fn is_weekend(&self) -> bool {
// ...
true
}
}
/* date_bindings.rs */
use pyo3::prelude::*;
use my_library::Date;
#[pyclass(name = "Date")]
struct DatePy {
_date: Date
}
#[pymethods]
impl DatePy {
#[new]
fn new(year: u16, month: u8, day: u8) -> Self {
DatePy { _date: Date{year, month, day} }
}
fn add_days(&self, N: u8) -> PyResult<Self> {
Ok(DatePy{_date: self._date.add_days(N)})
}
fn is_weekend(&self) -> PyResult<bool> {
Ok(self._date.is_weekend())
}
#[getter]
fn get_year(&self) -> PyResult<u16> {
Ok(self._date.year)
}
#[getter]
fn get_month(&self) -> PyResult<u8> {
Ok(self._date.month)
}
#[getter]
fn get_day(&self) -> PyResult<u8> {
Ok(self._date.day)
}
#[setter]
fn set_year(&mut self, year: u16) -> PyResult<()> {
self._date.year = year;
Ok(())
}
#[setter]
fn set_month(&mut self, month: u8) -> PyResult<()> {
self._date.month = month;
Ok(())
}
#[setter]
fn set_day(&mut self, day: u8) -> PyResult<()> {
self._date.day = day;
Ok(())
}
}
#[pymodule]
fn date_py(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<DatePy>()?;
Ok(())
}
Upvotes: 0
Views: 159