[smart_holder] Unique ptr deleter roundtrip tests and fix (#4921)

* Roundtrip through unique pointer with custom deleter.

Currently failing.

* Ensure the custom deleter is copied back to the unique pointer.

Feels like there's still a gap around the raw pointer flavour, but this at least
makes the unit test of the previous commit succeed.

* Add deleter roundtrip for const atyp.

Currently failing, custom deleter is lost.

* Fix storing deleter for const unique ptr.

Unit test from the previous commit passes.

* Remove SFINEA deleter assignment.

At the construction of the smart holder, it is either a del_fun, or a default constructed deleter, so this complexity is unnecessary.

* Clang format.

* Fixes for ci.

Clang 3.6 requires the extra constructors in the custom_deleter.

* fix(smart_holder): Loosen requirement on deleter to be default constructible.

And some other PR feedback.

* fix(smart_holder): Custom deleter in unit tests traces constructions.

* fix(smart_holder): Use pybind11_fail instead of assert.

* fix(smart_holder): Add unit tests for the default constructible deleter.

* fix(smart_holder): Use regex matching for deleter constructors in unit tests.
This commit is contained in:
Ivor Wanders
2023-11-08 01:58:24 -05:00
committed by GitHub
parent e02fe001cd
commit e5ce9631b1
3 changed files with 132 additions and 3 deletions

View File

@@ -464,6 +464,27 @@ struct shared_ptr_trampoline_self_life_support {
}
};
template <typename T,
typename D,
typename std::enable_if<std::is_default_constructible<D>::value, int>::type = 0>
inline std::unique_ptr<T, D> unique_with_deleter(T *raw_ptr, std::unique_ptr<D> &&deleter) {
if (deleter == nullptr) {
return std::unique_ptr<T, D>(raw_ptr);
}
return std::unique_ptr<T, D>(raw_ptr, std::move(*deleter));
}
template <typename T,
typename D,
typename std::enable_if<!std::is_default_constructible<D>::value, int>::type = 0>
inline std::unique_ptr<T, D> unique_with_deleter(T *raw_ptr, std::unique_ptr<D> &&deleter) {
if (deleter == nullptr) {
pybind11_fail("smart_holder_type_casters: deleter is not default constructible and no"
" instance available to return.");
}
return std::unique_ptr<T, D>(raw_ptr, std::move(*deleter));
}
template <typename T>
struct smart_holder_type_caster_load {
using holder_type = pybindit::memory::smart_holder;
@@ -589,7 +610,7 @@ struct smart_holder_type_caster_load {
" std::unique_ptr.");
}
if (!have_holder()) {
return nullptr;
return unique_with_deleter<T, D>(nullptr, std::unique_ptr<D>());
}
throw_if_uninitialized_or_disowned_holder();
throw_if_instance_is_currently_owned_by_shared_ptr();
@@ -616,13 +637,30 @@ struct smart_holder_type_caster_load {
"instance cannot safely be transferred to C++.");
}
// Temporary variable to store the extracted deleter in.
std::unique_ptr<D> extracted_deleter;
auto *gd = std::get_deleter<pybindit::memory::guarded_delete>(holder().vptr);
if (gd && gd->use_del_fun) { // Note the ensure_compatible_rtti_uqp_del<T, D>() call above.
// In smart_holder_poc, a custom deleter is always stored in a guarded delete.
// The guarded delete's std::function<void(void*)> actually points at the
// custom_deleter type, so we can verify it is of the custom deleter type and
// finally extract its deleter.
using custom_deleter_D = pybindit::memory::custom_deleter<T, D>;
const auto &custom_deleter_ptr = gd->del_fun.template target<custom_deleter_D>();
assert(custom_deleter_ptr != nullptr);
// Now that we have confirmed the type of the deleter matches the desired return
// value we can extract the function.
extracted_deleter = std::unique_ptr<D>(new D(std::move(custom_deleter_ptr->deleter)));
}
// Critical transfer-of-ownership section. This must stay together.
if (self_life_support != nullptr) {
holder().disown();
} else {
holder().release_ownership();
}
auto result = std::unique_ptr<T, D>(raw_type_ptr);
auto result = unique_with_deleter<T, D>(raw_type_ptr, std::move(extracted_deleter));
if (self_life_support != nullptr) {
self_life_support->activate_life_support(load_impl.loaded_v_h);
} else {
@@ -1056,7 +1094,8 @@ struct smart_holder_type_caster<std::unique_ptr<T const, D>>
static handle
cast(std::unique_ptr<T const, D> &&src, return_value_policy policy, handle parent) {
return smart_holder_type_caster<std::unique_ptr<T, D>>::cast(
std::unique_ptr<T, D>(const_cast<T *>(src.release())), // Const2Mutbl
std::unique_ptr<T, D>(const_cast<T *>(src.release()),
std::move(src.get_deleter())), // Const2Mutbl
policy,
parent);
}