|
| 1 | +#include <pybind11/functional.h> |
| 2 | +#include <pybind11/stl.h> |
| 3 | +#include <pybind11/type_caster_pyobject_ptr.h> |
| 4 | + |
| 5 | +#include "pybind11_tests.h" |
| 6 | + |
| 7 | +#include <cstddef> |
| 8 | +#include <vector> |
| 9 | + |
| 10 | +namespace { |
| 11 | + |
| 12 | +std::vector<PyObject *> make_vector_pyobject_ptr(const py::object &ValueHolder) { |
| 13 | + std::vector<PyObject *> vec_obj; |
| 14 | + for (int i = 1; i < 3; i++) { |
| 15 | + vec_obj.push_back(ValueHolder(i * 93).release().ptr()); |
| 16 | + } |
| 17 | + // This vector now owns the refcounts. |
| 18 | + return vec_obj; |
| 19 | +} |
| 20 | + |
| 21 | +} // namespace |
| 22 | + |
| 23 | +TEST_SUBMODULE(type_caster_pyobject_ptr, m) { |
| 24 | + m.def("cast_from_pyobject_ptr", []() { |
| 25 | + PyObject *ptr = PyLong_FromLongLong(6758L); |
| 26 | + return py::cast(ptr, py::return_value_policy::take_ownership); |
| 27 | + }); |
| 28 | + m.def("cast_to_pyobject_ptr", [](py::handle obj) { |
| 29 | + auto rc1 = obj.ref_count(); |
| 30 | + auto *ptr = py::cast<PyObject *>(obj); |
| 31 | + auto rc2 = obj.ref_count(); |
| 32 | + if (rc2 != rc1 + 1) { |
| 33 | + return -1; |
| 34 | + } |
| 35 | + return 100 - py::reinterpret_steal<py::object>(ptr).attr("value").cast<int>(); |
| 36 | + }); |
| 37 | + |
| 38 | + m.def( |
| 39 | + "return_pyobject_ptr", |
| 40 | + []() { return PyLong_FromLongLong(2314L); }, |
| 41 | + py::return_value_policy::take_ownership); |
| 42 | + m.def("pass_pyobject_ptr", [](PyObject *ptr) { |
| 43 | + return 200 - py::reinterpret_borrow<py::object>(ptr).attr("value").cast<int>(); |
| 44 | + }); |
| 45 | + |
| 46 | + m.def("call_callback_with_object_return", |
| 47 | + [](const std::function<py::object(int)> &cb, int value) { return cb(value); }); |
| 48 | + m.def( |
| 49 | + "call_callback_with_pyobject_ptr_return", |
| 50 | + [](const std::function<PyObject *(int)> &cb, int value) { return cb(value); }, |
| 51 | + py::return_value_policy::take_ownership); |
| 52 | + m.def( |
| 53 | + "call_callback_with_pyobject_ptr_arg", |
| 54 | + [](const std::function<int(PyObject *)> &cb, py::handle obj) { return cb(obj.ptr()); }, |
| 55 | + py::arg("cb"), // This triggers return_value_policy::automatic_reference |
| 56 | + py::arg("obj")); |
| 57 | + |
| 58 | + m.def("cast_to_pyobject_ptr_nullptr", [](bool set_error) { |
| 59 | + if (set_error) { |
| 60 | + PyErr_SetString(PyExc_RuntimeError, "Reflective of healthy error handling."); |
| 61 | + } |
| 62 | + PyObject *ptr = nullptr; |
| 63 | + py::cast(ptr); |
| 64 | + }); |
| 65 | + |
| 66 | + m.def("cast_to_pyobject_ptr_non_nullptr_with_error_set", []() { |
| 67 | + PyErr_SetString(PyExc_RuntimeError, "Reflective of unhealthy error handling."); |
| 68 | + py::cast(Py_None); |
| 69 | + }); |
| 70 | + |
| 71 | + m.def("pass_list_pyobject_ptr", [](const std::vector<PyObject *> &vec_obj) { |
| 72 | + int acc = 0; |
| 73 | + for (const auto &ptr : vec_obj) { |
| 74 | + acc = acc * 1000 + py::reinterpret_borrow<py::object>(ptr).attr("value").cast<int>(); |
| 75 | + } |
| 76 | + return acc; |
| 77 | + }); |
| 78 | + |
| 79 | + m.def("return_list_pyobject_ptr_take_ownership", |
| 80 | + make_vector_pyobject_ptr, |
| 81 | + // Ownership is transferred one-by-one when the vector is converted to a Python list. |
| 82 | + py::return_value_policy::take_ownership); |
| 83 | + |
| 84 | + m.def("return_list_pyobject_ptr_reference", |
| 85 | + make_vector_pyobject_ptr, |
| 86 | + // Ownership is not transferred. |
| 87 | + py::return_value_policy::reference); |
| 88 | + |
| 89 | + m.def("dec_ref_each_pyobject_ptr", [](const std::vector<PyObject *> &vec_obj) { |
| 90 | + std::size_t i = 0; |
| 91 | + for (; i < vec_obj.size(); i++) { |
| 92 | + py::handle h(vec_obj[i]); |
| 93 | + if (static_cast<std::size_t>(h.ref_count()) < 2) { |
| 94 | + break; // Something is badly wrong. |
| 95 | + } |
| 96 | + h.dec_ref(); |
| 97 | + } |
| 98 | + return i; |
| 99 | + }); |
| 100 | + |
| 101 | +#ifdef PYBIND11_NO_COMPILE_SECTION // Change to ifndef for manual testing. |
| 102 | + { |
| 103 | + PyObject *ptr = nullptr; |
| 104 | + (void) py::cast(*ptr); |
| 105 | + } |
| 106 | +#endif |
| 107 | +} |
0 commit comments