diff --git a/include/pybind11/smart_holder_poc.h b/include/pybind11/smart_holder_poc.h new file mode 100644 index 000000000..1f14fba92 --- /dev/null +++ b/include/pybind11/smart_holder_poc.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +struct smart_holder { + std::shared_ptr vptr; + const std::type_info* rtti_held; + const std::type_info* rtti_uqp_del; + bool have_external_shp; + + smart_holder() + : rtti_held{nullptr}, rtti_uqp_del{nullptr}, have_external_shp(false) {} + + template + void ensure_compatible_rtti(const char* context) { + const std::type_info* rtti_requested = &typeid(T); + if (!(*rtti_requested == *rtti_held)) { + throw std::runtime_error(std::string("Incompatible RTTI (") + context + + ")."); + } + } + + void ensure_use_count_1(const char* context) { + if (vptr.use_count() != 1) { + throw std::runtime_error(std::string("Cannot disown use_count != 1 (") + + context + ")."); + } + } + + void ensure_unique_ptr_default_deleter(const char* context) { + if (rtti_uqp_del != nullptr) { + throw std::runtime_error( + std::string("Cannot disown unique_ptr deleter (") + context + ")."); + } + } + + void ensure_internal_shared_ptr(const char* context) { + if (have_external_shp) { + throw std::runtime_error( + std::string("Cannot disown external shared_ptr (") + context + ")."); + } + } + + template + void from_raw_ptr_owned(T* raw_ptr) { + vptr.reset(raw_ptr); + rtti_held = &typeid(T); + } + + template + T* as_raw_ptr_owned() { + static const char* context = "as_raw_ptr_owned"; + ensure_compatible_rtti(context); + ensure_use_count_1(context); + ensure_unique_ptr_default_deleter(context); + ensure_internal_shared_ptr(context); + std::shared_ptr tptr = std::static_pointer_cast(vptr); + vptr.reset(); + T* result = tptr.get(); + // TODO tptr.release(); + return result; + } + + template + std::shared_ptr as_shared_ptr() { + static const char* context = "as_shared_ptr"; + ensure_compatible_rtti(context); + return std::static_pointer_cast(vptr); + } +}; + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/tests/test_smart_holder_poc.cpp b/tests/test_smart_holder_poc.cpp new file mode 100644 index 000000000..09db276bb --- /dev/null +++ b/tests/test_smart_holder_poc.cpp @@ -0,0 +1,32 @@ +#include "pybind11_tests.h" + +#include + +#include +#include + +namespace pybind11_tests { +namespace smart_holder_poc { + +inline void to_cout(std::string msg) { std::cout << msg << std::endl; } + +inline void exercise() { + to_cout(""); + namespace py = pybind11; + py::smart_holder hld; + hld.from_raw_ptr_owned(new int(13)); + to_cout(hld.rtti_held->name()); + { + std::shared_ptr val = hld.as_shared_ptr(); + to_cout(std::to_string(*val)); + } + { + std::unique_ptr val(hld.as_raw_ptr_owned()); + to_cout(std::to_string(*val)); + } +} + +TEST_SUBMODULE(smart_holder_poc, m) { m.def("exercise", exercise); } + +} // namespace smart_holder_poc +} // namespace pybind11_tests diff --git a/tests/test_smart_holder_poc.py b/tests/test_smart_holder_poc.py new file mode 100644 index 000000000..1606e0cf4 --- /dev/null +++ b/tests/test_smart_holder_poc.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +import pytest + +from pybind11_tests import smart_holder_poc as m + + +def test_exercise(): + m.exercise()