diff --git a/include/pybind11/detail/classh_type_casters.h b/include/pybind11/detail/classh_type_casters.h index 033a5ead1..56790744d 100644 --- a/include/pybind11/detail/classh_type_casters.h +++ b/include/pybind11/detail/classh_type_casters.h @@ -353,10 +353,10 @@ struct classh_type_caster : smart_holder_type_caster_load { // clang-format off - operator T() { return this->loaded_smhldr_ptr->template lvalue_ref(); } - operator T&&() && { return this->loaded_smhldr_ptr->template rvalue_ref(); } - operator T const&() { return this->loaded_smhldr_ptr->template lvalue_ref(); } - operator T&() { return this->loaded_smhldr_ptr->template lvalue_ref(); } + operator T() { return this->loaded_smhldr_ptr->template as_lvalue_ref(); } + operator T&&() && { return this->loaded_smhldr_ptr->template as_rvalue_ref(); } + operator T const&() { return this->loaded_smhldr_ptr->template as_lvalue_ref(); } + operator T&() { return this->loaded_smhldr_ptr->template as_lvalue_ref(); } operator T const*() { return this->loaded_as_raw_ptr_unowned(); } operator T*() { return this->loaded_as_raw_ptr_unowned(); } diff --git a/include/pybind11/smart_holder_poc.h b/include/pybind11/smart_holder_poc.h index d7145fd76..58e4bd587 100644 --- a/include/pybind11/smart_holder_poc.h +++ b/include/pybind11/smart_holder_poc.h @@ -12,7 +12,13 @@ High-level aspects: C++ Undefined Behavior, especially from Python. * Support a system design with clean runtime inheritance casting. From this - it follows that the `smart_holder` needs to be type-erased (`void*`, RTTI). + it follows that the `smart_holder` needs to be type-erased (`void*`). + +* Handling of RTTI for the type-erased held pointer is NOT implemented here. + It is the responsibility of the caller to ensure that `static_cast` + is well-formed when calling `as_*` member functions. Inheritance casting + needs to be handled in a different layer (similar to the code organization + in boost/python/object/inheritance.hpp). Details: @@ -28,10 +34,6 @@ Details: * If created from an external `shared_ptr`, or a `unique_ptr` with a custom deleter, including life-time management for external objects is infeasible. -* The `typename T` between `from` and `as` calls must match exactly. - Inheritance casting needs to be handled in a different layer (similar - to the code organization in boost/python/object/inheritance.hpp). - * The smart_holder is movable but not copyable, as a consequence of using unique_ptr for the vptr_deleter_armed_flag_ptr. Note that the bool for the flag has to live on the heap, for the smart_holder to be movable. @@ -72,35 +74,30 @@ struct guarded_custom_deleter { }; struct smart_holder { - const std::type_info *rtti_held; const std::type_info *rtti_uqp_del; std::unique_ptr vptr_deleter_armed_flag_ptr; std::shared_ptr vptr; bool vptr_is_using_noop_deleter : 1; bool vptr_is_using_builtin_delete : 1; bool vptr_is_external_shared_ptr : 1; + bool is_populated : 1; smart_holder() - : rtti_held{nullptr}, rtti_uqp_del{nullptr}, vptr_is_using_noop_deleter{false}, - vptr_is_using_builtin_delete{false}, vptr_is_external_shared_ptr{false} {} + : rtti_uqp_del{nullptr}, vptr_is_using_noop_deleter{false}, + vptr_is_using_builtin_delete{false}, vptr_is_external_shared_ptr{false}, is_populated{ + false} {} explicit smart_holder(bool vptr_deleter_armed_flag) - : rtti_held{nullptr}, rtti_uqp_del{nullptr}, vptr_deleter_armed_flag_ptr{new bool{ - vptr_deleter_armed_flag}}, + : rtti_uqp_del{nullptr}, vptr_deleter_armed_flag_ptr{new bool{vptr_deleter_armed_flag}}, vptr_is_using_noop_deleter{false}, vptr_is_using_builtin_delete{false}, - vptr_is_external_shared_ptr{false} {} + vptr_is_external_shared_ptr{false}, is_populated{false} {} bool has_pointee() const { return vptr.get() != nullptr; } - template - void ensure_compatible_rtti_held(const char *context) const { - if (!rtti_held) { + void ensure_is_populated(const char *context) const { + if (!is_populated) { throw std::runtime_error(std::string("Unpopulated holder (") + context + ")."); } - const std::type_info *rtti_requested = &typeid(T); - if (!(*rtti_requested == *rtti_held)) { - throw std::runtime_error(std::string("Incompatible type (") + context + ")."); - } } template @@ -153,50 +150,47 @@ struct smart_holder { template static smart_holder from_raw_ptr_unowned(T *raw_ptr) { smart_holder hld(false); - hld.rtti_held = &typeid(T); hld.vptr.reset(raw_ptr, guarded_builtin_delete(hld.vptr_deleter_armed_flag_ptr.get())); hld.vptr_is_using_noop_deleter = true; + hld.is_populated = true; return hld; } template T *as_raw_ptr_unowned() const { - static const char *context = "as_raw_ptr_unowned"; - ensure_compatible_rtti_held(context); return static_cast(vptr.get()); } template - T &lvalue_ref() const { - static const char *context = "lvalue_ref"; - ensure_compatible_rtti_held(context); + T &as_lvalue_ref() const { + static const char *context = "as_lvalue_ref"; + ensure_is_populated(context); ensure_has_pointee(context); - return *static_cast(vptr.get()); + return *as_raw_ptr_unowned(); } template - T &&rvalue_ref() const { - static const char *context = "rvalue_ref"; - ensure_compatible_rtti_held(context); + T &&as_rvalue_ref() const { + static const char *context = "as_rvalue_ref"; + ensure_is_populated(context); ensure_has_pointee(context); - return std::move(*static_cast(vptr.get())); + return std::move(*as_raw_ptr_unowned()); } template static smart_holder from_raw_ptr_take_ownership(T *raw_ptr) { smart_holder hld(true); - hld.rtti_held = &typeid(T); hld.vptr.reset(raw_ptr, guarded_builtin_delete(hld.vptr_deleter_armed_flag_ptr.get())); hld.vptr_is_using_builtin_delete = true; + hld.is_populated = true; return hld; } template T *as_raw_ptr_release_ownership(const char *context = "as_raw_ptr_release_ownership") { - ensure_compatible_rtti_held(context); ensure_vptr_is_using_builtin_delete(context); ensure_use_count_1(context); - T *raw_ptr = static_cast(vptr.get()); + T *raw_ptr = as_raw_ptr_unowned(); *vptr_deleter_armed_flag_ptr = false; vptr.reset(); return raw_ptr; @@ -205,11 +199,11 @@ struct smart_holder { template static smart_holder from_unique_ptr(std::unique_ptr &&unq_ptr) { smart_holder hld(true); - hld.rtti_held = &typeid(T); hld.vptr.reset(unq_ptr.get(), guarded_builtin_delete(hld.vptr_deleter_armed_flag_ptr.get())); unq_ptr.release(); hld.vptr_is_using_builtin_delete = true; + hld.is_populated = true; return hld; } @@ -221,21 +215,20 @@ struct smart_holder { template static smart_holder from_unique_ptr_with_deleter(std::unique_ptr &&unq_ptr) { smart_holder hld(true); - hld.rtti_held = &typeid(T); hld.rtti_uqp_del = &typeid(D); hld.vptr.reset(unq_ptr.get(), guarded_custom_deleter(hld.vptr_deleter_armed_flag_ptr.get())); unq_ptr.release(); + hld.is_populated = true; return hld; } template std::unique_ptr as_unique_ptr_with_deleter() { static const char *context = "as_unique_ptr_with_deleter"; - ensure_compatible_rtti_held(context); ensure_compatible_rtti_uqp_del(context); ensure_use_count_1(context); - T *raw_ptr = static_cast(vptr.get()); + T *raw_ptr = as_raw_ptr_unowned(); *vptr_deleter_armed_flag_ptr = false; vptr.reset(); return std::unique_ptr(raw_ptr); @@ -244,16 +237,14 @@ struct smart_holder { template static smart_holder from_shared_ptr(std::shared_ptr shd_ptr) { smart_holder hld; - hld.rtti_held = &typeid(T); hld.vptr = std::static_pointer_cast(shd_ptr); hld.vptr_is_external_shared_ptr = true; + hld.is_populated = true; return hld; } template std::shared_ptr as_shared_ptr() const { - static const char *context = "as_shared_ptr"; - ensure_compatible_rtti_held(context); return std::static_pointer_cast(vptr); } }; diff --git a/tests/core/smart_holder_poc_test.cpp b/tests/core/smart_holder_poc_test.cpp index e97cadbc9..a4123b915 100644 --- a/tests/core/smart_holder_poc_test.cpp +++ b/tests/core/smart_holder_poc_test.cpp @@ -32,17 +32,17 @@ TEST_CASE("from_raw_ptr_unowned+as_raw_ptr_unowned", "[S]") { REQUIRE(*hld.as_raw_ptr_unowned() == 19); } -TEST_CASE("from_raw_ptr_unowned+lvalue_ref", "[S]") { +TEST_CASE("from_raw_ptr_unowned+as_lvalue_ref", "[S]") { static int value = 19; auto hld = smart_holder::from_raw_ptr_unowned(&value); - REQUIRE(hld.lvalue_ref() == 19); + REQUIRE(hld.as_lvalue_ref() == 19); } -TEST_CASE("from_raw_ptr_unowned+rvalue_ref", "[S]") { +TEST_CASE("from_raw_ptr_unowned+as_rvalue_ref", "[S]") { helpers::movable_int orig(19); { auto hld = smart_holder::from_raw_ptr_unowned(&orig); - helpers::movable_int othr(hld.rvalue_ref()); + helpers::movable_int othr(hld.as_rvalue_ref()); REQUIRE(othr.valu == 19); REQUIRE(orig.valu == 91); } @@ -78,10 +78,10 @@ TEST_CASE("from_raw_ptr_unowned+as_shared_ptr", "[S]") { REQUIRE(*hld.as_shared_ptr() == 19); } -TEST_CASE("from_raw_ptr_take_ownership+lvalue_ref", "[S]") { +TEST_CASE("from_raw_ptr_take_ownership+as_lvalue_ref", "[S]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); REQUIRE(hld.has_pointee()); - REQUIRE(hld.lvalue_ref() == 19); + REQUIRE(hld.as_lvalue_ref() == 19); } TEST_CASE("from_raw_ptr_take_ownership+as_raw_ptr_release_ownership1", "[S]") { @@ -127,11 +127,11 @@ TEST_CASE("from_raw_ptr_take_ownership+as_shared_ptr", "[S]") { REQUIRE(*new_owner == 19); } -TEST_CASE("from_unique_ptr+lvalue_ref", "[S]") { +TEST_CASE("from_unique_ptr+as_lvalue_ref", "[S]") { std::unique_ptr orig_owner(new int(19)); auto hld = smart_holder::from_unique_ptr(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - REQUIRE(hld.lvalue_ref() == 19); + REQUIRE(hld.as_lvalue_ref() == 19); } TEST_CASE("from_unique_ptr+as_raw_ptr_release_ownership1", "[S]") { @@ -189,11 +189,11 @@ TEST_CASE("from_unique_ptr+as_shared_ptr", "[S]") { REQUIRE(*new_owner == 19); } -TEST_CASE("from_unique_ptr_with_deleter+lvalue_ref", "[S]") { +TEST_CASE("from_unique_ptr_with_deleter+as_lvalue_ref", "[S]") { std::unique_ptr> orig_owner(new int(19)); auto hld = smart_holder::from_unique_ptr_with_deleter(std::move(orig_owner)); REQUIRE(orig_owner.get() == nullptr); - REQUIRE(hld.lvalue_ref() == 19); + REQUIRE(hld.as_lvalue_ref() == 19); } TEST_CASE("from_unique_ptr_with_deleter+as_raw_ptr_release_ownership", "[E]") { @@ -241,10 +241,10 @@ TEST_CASE("from_unique_ptr_with_deleter+as_shared_ptr", "[S]") { REQUIRE(*new_owner == 19); } -TEST_CASE("from_shared_ptr+lvalue_ref", "[S]") { +TEST_CASE("from_shared_ptr+as_lvalue_ref", "[S]") { std::shared_ptr orig_owner(new int(19)); auto hld = smart_holder::from_shared_ptr(orig_owner); - REQUIRE(hld.lvalue_ref() == 19); + REQUIRE(hld.as_lvalue_ref() == 19); } TEST_CASE("from_shared_ptr+as_raw_ptr_release_ownership", "[E]") { @@ -279,19 +279,13 @@ TEST_CASE("from_shared_ptr+as_shared_ptr", "[S]") { TEST_CASE("error_unpopulated_holder", "[E]") { smart_holder hld; - REQUIRE_THROWS_WITH(hld.as_raw_ptr_unowned(), "Unpopulated holder (as_raw_ptr_unowned)."); -} - -TEST_CASE("error_incompatible_type", "[E]") { - static int value = 19; - auto hld = smart_holder::from_raw_ptr_unowned(&value); - REQUIRE_THROWS_WITH(hld.as_unique_ptr(), "Incompatible type (as_unique_ptr)."); + REQUIRE_THROWS_WITH(hld.as_lvalue_ref(), "Unpopulated holder (as_lvalue_ref)."); } TEST_CASE("error_disowned_holder", "[E]") { auto hld = smart_holder::from_raw_ptr_take_ownership(new int(19)); hld.as_unique_ptr(); - REQUIRE_THROWS_WITH(hld.lvalue_ref(), "Disowned holder (lvalue_ref)."); + REQUIRE_THROWS_WITH(hld.as_lvalue_ref(), "Disowned holder (as_lvalue_ref)."); } TEST_CASE("error_cannot_disown_nullptr", "[E]") {