diff --git a/include/pybind11/smart_holder_poc.h b/include/pybind11/smart_holder_poc.h index 8216eaeab..efc89cea4 100644 --- a/include/pybind11/smart_holder_poc.h +++ b/include/pybind11/smart_holder_poc.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include namespace pybindit { @@ -44,8 +46,10 @@ struct smart_holder { rtti_uqp_del{nullptr}, vptr_deleter_guard_flag{false} {} + bool has_pointee() const { return vptr.get() != nullptr; } + template - void ensure_compatible_rtti_held(const char* context) { + void ensure_compatible_rtti_held(const char* context) const { const std::type_info* rtti_requested = &typeid(T); if (!(*rtti_requested == *rtti_held)) { throw std::runtime_error(std::string("Incompatible RTTI (") + context + @@ -54,7 +58,7 @@ struct smart_holder { } template - void ensure_compatible_rtti_uqp_del(const char* context) { + void ensure_compatible_rtti_uqp_del(const char* context) const { const std::type_info* rtti_requested = &typeid(D); if (!(*rtti_requested == *rtti_uqp_del)) { throw std::runtime_error( @@ -62,14 +66,21 @@ struct smart_holder { } } - void ensure_vptr_deleter_guard_flag_true(const char* context) { + void ensure_has_pointee(const char* context) const { + if (!has_pointee()) { + throw std::runtime_error(std::string("Disowned holder (") + context + + ")."); + } + } + + void ensure_vptr_deleter_guard_flag_true(const char* context) const { if (rtti_uqp_del != nullptr) { throw std::runtime_error(std::string("Cannot disown this shared_ptr (") + context + ")."); } } - void ensure_use_count_1(const char* context) { + void ensure_use_count_1(const char* context) const { if (vptr.use_count() != 1) { throw std::runtime_error(std::string("Cannot disown use_count != 1 (") + context + ")."); @@ -77,7 +88,15 @@ struct smart_holder { } template - void from_raw_ptr_owned(T* raw_ptr) { + const T& const_value_ref() const { + static const char* context = "const_value_ref"; + ensure_compatible_rtti_held(context); + ensure_has_pointee(context); + return *static_cast(vptr.get()); + } + + template + void from_raw_ptr_take_ownership(T* raw_ptr) { clear(); rtti_held = &typeid(T); vptr_deleter_guard_flag = true; @@ -93,7 +112,8 @@ struct smart_holder { } template - T* as_raw_ptr_owned(const char* context = "as_raw_ptr_owned") { + T* as_raw_ptr_release_ownership( + const char* context = "as_raw_ptr_release_ownership") { ensure_compatible_rtti_held(context); ensure_vptr_deleter_guard_flag_true(context); ensure_use_count_1(context); @@ -104,7 +124,7 @@ struct smart_holder { } template - T* as_raw_ptr_unowned() { + T* as_raw_ptr_unowned() const { static const char* context = "as_raw_ptr_unowned"; ensure_compatible_rtti_held(context); return static_cast(vptr.get()); @@ -122,7 +142,7 @@ struct smart_holder { template std::unique_ptr as_unique_ptr() { - return std::unique_ptr(as_raw_ptr_owned("as_unique_ptr")); + return std::unique_ptr(as_raw_ptr_release_ownership("as_unique_ptr")); } template @@ -156,7 +176,7 @@ struct smart_holder { } template - std::shared_ptr as_shared_ptr() { + 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 new file mode 100644 index 000000000..6f91b6d18 --- /dev/null +++ b/tests/core/smart_holder_poc_test.cpp @@ -0,0 +1,99 @@ +#include "pybind11/smart_holder_poc.h" + +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +using pybindit::memory::smart_holder; + +namespace helpers { + +template +struct functor_builtin_delete { + void operator()(T* ptr) { delete ptr; } +}; + +} // namespace helpers + +TEST_CASE("from_raw_ptr_take_ownership=const_value_ref") { + smart_holder hld; + REQUIRE(!hld.has_pointee()); + hld.from_raw_ptr_take_ownership(new int(19)); + REQUIRE(hld.has_pointee()); + REQUIRE(hld.const_value_ref() == 19); +} + +TEST_CASE("from_raw_ptr_unowned=const_value_ref") { + static int value = 19; + smart_holder hld; + hld.from_raw_ptr_unowned(&value); + REQUIRE(hld.const_value_ref() == 19); +} + +TEST_CASE("as_raw_ptr_release_ownership") { + smart_holder hld; + hld.from_raw_ptr_take_ownership(new int(19)); + auto new_owner = + std::unique_ptr(hld.as_raw_ptr_release_ownership()); + REQUIRE(!hld.has_pointee()); +} + +TEST_CASE("as_raw_ptr_unowned") { + smart_holder hld; + hld.from_raw_ptr_take_ownership(new int(19)); + int* raw_ptr = hld.as_raw_ptr_unowned(); + REQUIRE(hld.has_pointee()); + REQUIRE(*raw_ptr == 19); +} + +TEST_CASE("from_unique_ptr=const_value_ref") { + std::unique_ptr orig_owner(new int(19)); + smart_holder hld; + hld.from_unique_ptr(std::move(orig_owner)); + REQUIRE(orig_owner.get() == nullptr); + REQUIRE(hld.const_value_ref() == 19); +} + +TEST_CASE("as_unique_ptr") { + smart_holder hld; + hld.from_raw_ptr_take_ownership(new int(19)); + auto new_owner = hld.as_unique_ptr(); + REQUIRE(!hld.has_pointee()); + REQUIRE(*new_owner == 19); +} + +TEST_CASE("from_unique_ptr_with_deleter=const_value_ref") { + std::unique_ptr> orig_owner( + new int(19)); + smart_holder hld; + hld.from_unique_ptr_with_deleter(std::move(orig_owner)); + REQUIRE(orig_owner.get() == nullptr); + REQUIRE(hld.const_value_ref() == 19); +} + +TEST_CASE("as_unique_ptr_with_deleter") { + std::unique_ptr> orig_owner( + new int(19)); + smart_holder hld; + hld.from_unique_ptr_with_deleter(std::move(orig_owner)); + auto new_owner = + hld.as_unique_ptr_with_deleter>(); + REQUIRE(!hld.has_pointee()); + REQUIRE(*new_owner == 19); +} + +TEST_CASE("from_shared_ptr=const_value_ref") { + std::shared_ptr orig_owner(new int(19)); + smart_holder hld; + hld.from_shared_ptr(orig_owner); + REQUIRE(orig_owner.get() != nullptr); + REQUIRE(hld.const_value_ref() == 19); +} + +TEST_CASE("as_shared_ptr") { + smart_holder hld; + hld.from_raw_ptr_take_ownership(new int(19)); + auto new_owner = hld.as_shared_ptr(); + REQUIRE(hld.has_pointee()); + REQUIRE(*new_owner == 19); +}