Skip to content

Instantly share code, notes, and snippets.

@battleguard
Created August 25, 2023 17:43
Show Gist options
  • Save battleguard/a953e703279ebcb3425f611a187f8326 to your computer and use it in GitHub Desktop.
Save battleguard/a953e703279ebcb3425f611a187f8326 to your computer and use it in GitHub Desktop.
#include <iostream>
#define PYBIND11_HANDLE_REF_DEBUG
#include <pybind11/pybind11.h>
#include <pybind11/operators.h>
namespace py = pybind11;
using namespace pybind11::literals;
struct Platform
{
Platform()
{
static int platformCount = 1;
Index = platformCount++;
std::cout << "Platform CTOR Idx: " << Index << " Addr: " << this << std::endl;
}
~Platform()
{
std::cout << "Platform DTOR Idx: " << Index << " Addr: " << this << std::endl;
Index = 0;
}
int Index;
};
template<typename T>
[[nodiscard]] std::unique_ptr<T> RelinquishPythonOwnship(T* aPythonObject, bool aErrorOnFail = true)
{
const void* key = reinterpret_cast<const void*>(aPythonObject);
const auto instances = pybind11::detail::get_internals().registered_instances.equal_range(key);
for (auto it = instances.first; it != instances.second; ++it)
{
pybind11::detail::instance* instance = it->second;
if(instance->owned || instance->simple_holder_constructed)
{
instance->owned = false;
instance->simple_holder_constructed = false;
return std::unique_ptr<T>(aPythonObject);
}
}
if(aErrorOnFail)
{
py::pybind11_fail("Failed to RelinquishPythonOwnship on passed in python object");
}
return std::unique_ptr<T>(nullptr);
}
struct Collection
{
Collection()
{
static int Counter = 1;
Index = Counter++;
std::cout << "Collection CTOR Idx: " << Index << " Addr: " << this << std::endl;
}
~Collection()
{
std::cout << "Collection DTOR Idx: " << Index << " Addr: " << this << std::endl;
mOwnedPointers.clear();
Index = 0;
std::cout << "Collection DTOR done!" << std::endl;;
}
void AddPlatform(Platform* aPlatform)
{
py::print("Adding platform: ", aPlatform->Index);
std::unique_ptr<Platform> ownedPtr = RelinquishPythonOwnship(aPlatform);
mOwnedPointers.push_back(std::move(ownedPtr));
}
Platform& GetPlatformNonOwning()
{
return internal;
}
Platform* GetPlatform()
{
return mOwnedPointers.empty() ? nullptr : mOwnedPointers.front().get();
}
int Index;
Platform internal;
std::vector<std::unique_ptr<Platform>> mOwnedPointers;
};
PYBIND11_MODULE(pybind11_example, m)
{
py::class_<Platform, std::unique_ptr<Platform>> platform(m, "Platform");
platform.def(py::init());
platform.def_readonly("Index", &Platform::Index);
py::class_<Collection, std::unique_ptr<Collection>> sim(m, "Collection");
sim.def(py::init());
sim.def_readonly("Index", &Collection::Index);
sim.def("AddPlatform", &Collection::AddPlatform);
sim.def("GetPlatform", &Collection::GetPlatform, py::return_value_policy::reference);
sim.def("GetPlatformNonOwning", &Collection::GetPlatformNonOwning, py::return_value_policy::reference);
auto get_info = [](py::handle obj)
{
PyObject* py_obj = obj.ptr();
pybind11::detail::instance* instance_ptr = reinterpret_cast<pybind11::detail::instance*>(py_obj);
uintptr_t cpp_address = reinterpret_cast<uintptr_t>(obj.cast<void*>());
uintptr_t py_address = reinterpret_cast<uintptr_t>(obj.ptr());
bool owned = instance_ptr->owned;
const pybind11::detail::type_info* py_type_info = pybind11::detail::get_type_info(py_obj->ob_type);
py::str object_name(py_obj);
if (py_type_info)
{
object_name = py::str(py_type_info->cpptype->name());
}
return std::make_tuple(object_name, obj.ref_count(), cpp_address, py_address, owned);
};
auto print_info = [](const std::tuple<py::str, int, uintptr_t, uintptr_t, bool>& aData) -> void
{
auto hex = py::module::import("builtins").attr("hex");
py::print("name = ", std::get<0>(aData),
" refs = ", std::get<1>(aData),
" cpp_addr = ", hex(std::get<2>(aData)),
" py_addr = ", hex(std::get<3>(aData)),
" owned = ", std::get<4>(aData));
};
m.def("get_object_info", get_info);
m.def("print_info", print_info);
auto print_all_instances = [=]()
{
py::print("Current Pybind11 Registered Instances:");
auto& refs = pybind11::detail::get_internals().registered_instances;
for (const auto& [key, value] : refs)
{
PyObject* c_obj = _PyObject_CAST(value);
py::handle handle = py::handle(c_obj);
print_info(get_info(handle));
}
};
m.def("print_obj_info", [=](py::handle obj)
{
print_info(get_info(obj));
print_all_instances();
});
m.def("print_registered_instances", print_all_instances);
}
import pybind11_example
def main():
print("main")
platforms = pybind11_example.Collection()
platform1 = platforms.GetPlatformNonOwning()
platform2 = pybind11_example.Platform()
platform3 = pybind11_example.Platform()
pybind11_example.print_registered_instances()
platforms.AddPlatform(platform2)
try:
platforms.AddPlatform(platform1)
except RuntimeError as e:
print("Cannot add a non owning platform: ", platform1)
print(e)
finally:
pass
platform4 = platforms.GetPlatform()
pybind11_example.print_registered_instances()
print("end_main")
if __name__ == '__main__':
main()
pybind11_example.print_registered_instances()
print("Program Complete")
$ "C:/Program Files/Python310/python.exe" c:/working/py11_playground/pybind11_example/main.py
main
Platform CTOR Idx: 1 Addr: 0000024928AA84A4
Collection CTOR Idx: 1 Addr: 0000024928AA84A0
Platform CTOR Idx: 2 Addr: 0000024928AA31B0
Platform CTOR Idx: 3 Addr: 0000024928AA3110
Current Pybind11 Registered Instances:
name = struct Collection refs = 1 cpp_addr = 0x24928aa84a0 py_addr = 0x24929227870 owned = True
name = struct Platform refs = 1 cpp_addr = 0x24928aa84a4 py_addr = 0x24929227770 owned = False
name = struct Platform refs = 1 cpp_addr = 0x24928aa3110 py_addr = 0x249292357f0 owned = True
name = struct Platform refs = 1 cpp_addr = 0x24928aa31b0 py_addr = 0x249292275b0 owned = True
Adding platform: 2
Adding platform: 1
Cannot add a non owning platform: <pybind11_example.Platform object at 0x0000024929227770>
Failed to RelinquishPythonOwnship on passed in python object
Current Pybind11 Registered Instances:
name = struct Collection refs = 1 cpp_addr = 0x24928aa84a0 py_addr = 0x24929227870 owned = True
name = struct Platform refs = 1 cpp_addr = 0x24928aa84a4 py_addr = 0x24929227770 owned = False
name = struct Platform refs = 1 cpp_addr = 0x24928aa3110 py_addr = 0x249292357f0 owned = True
name = struct Platform refs = 2 cpp_addr = 0x24928aa31b0 py_addr = 0x249292275b0 owned = False
end_main
Collection DTOR Idx: 1 Addr: 0000024928AA84A0
Platform DTOR Idx: 2 Addr: 0000024928AA31B0
Collection DTOR done!
Platform DTOR Idx: 1 Addr: 0000024928AA84A4
Platform DTOR Idx: 3 Addr: 0000024928AA3110
Current Pybind11 Registered Instances:
Program Complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment