| // ######################################################################### | |
| // PLEASE UPDATE docs/advanced/cast/custom.rst IF ANY CHANGES ARE MADE HERE. | |
| // ######################################################################### | |
| #include "pybind11_tests.h" | |
| namespace user_space { | |
| struct Point2D { | |
| double x; | |
| double y; | |
| }; | |
| Point2D negate(const Point2D &point) { return Point2D{-point.x, -point.y}; } | |
| } // namespace user_space | |
| namespace pybind11 { | |
| namespace detail { | |
| template <> | |
| struct type_caster<user_space::Point2D> { | |
| // This macro inserts a lot of boilerplate code and sets the type hint. | |
| // `io_name` is used to specify different type hints for arguments and return values. | |
| // The signature of our negate function would then look like: | |
| // `negate(collections.abc.Sequence[float]) -> tuple[float, float]` | |
| PYBIND11_TYPE_CASTER(user_space::Point2D, | |
| io_name("collections.abc.Sequence[float]", "tuple[float, float]")); | |
| // C++ -> Python: convert `Point2D` to `tuple[float, float]`. The second and third arguments | |
| // are used to indicate the return value policy and parent object (for | |
| // return_value_policy::reference_internal) and are often ignored by custom casters. | |
| // The return value should reflect the type hint specified by the second argument of `io_name`. | |
| static handle | |
| cast(const user_space::Point2D &number, return_value_policy /*policy*/, handle /*parent*/) { | |
| return py::make_tuple(number.x, number.y).release(); | |
| } | |
| // Python -> C++: convert a `PyObject` into a `Point2D` and return false upon failure. The | |
| // second argument indicates whether implicit conversions should be allowed. | |
| // The accepted types should reflect the type hint specified by the first argument of | |
| // `io_name`. | |
| bool load(handle src, bool /*convert*/) { | |
| // Check if handle is a Sequence | |
| if (!py::isinstance<py::sequence>(src)) { | |
| return false; | |
| } | |
| auto seq = py::reinterpret_borrow<py::sequence>(src); | |
| // Check if exactly two values are in the Sequence | |
| if (seq.size() != 2) { | |
| return false; | |
| } | |
| // Check if each element is either a float or an int | |
| for (auto item : seq) { | |
| if (!py::isinstance<py::float_>(item) && !py::isinstance<py::int_>(item)) { | |
| return false; | |
| } | |
| } | |
| value.x = seq[0].cast<double>(); | |
| value.y = seq[1].cast<double>(); | |
| return true; | |
| } | |
| }; | |
| } // namespace detail | |
| } // namespace pybind11 | |
| // Bind the negate function | |
| TEST_SUBMODULE(docs_advanced_cast_custom, m) { m.def("negate", user_space::negate); } |