diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index d30621c88..33a1e8a12 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -431,6 +431,8 @@ inline void clear_instance(PyObject *self) { if (instance->owned || v_h.holder_constructed()) { v_h.type->dealloc(v_h); } + } else if (v_h.holder_constructed()) { + v_h.type->dealloc(v_h); // Disowned instance. } } // Deallocate the value/holder layout internals: diff --git a/include/pybind11/detail/init.h b/include/pybind11/detail/init.h index 4509bd131..9c09cfbcb 100644 --- a/include/pybind11/detail/init.h +++ b/include/pybind11/detail/init.h @@ -10,6 +10,7 @@ #pragma once #include "class.h" +#include "smart_holder_sfinae_hooks_only.h" PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) @@ -128,11 +129,14 @@ void construct(value_and_holder &v_h, Cpp *ptr, bool need_alias) { // the holder and destruction happens when we leave the C++ scope, and the holder // class gets to handle the destruction however it likes. v_h.value_ptr() = ptr; - v_h.set_instance_registered(true); // To prevent init_instance from registering it - v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder + v_h.set_instance_registered(true); // To prevent init_instance from registering it + v_h.set_instance_registered(true); // SHORTCUT To prevent init_instance from registering it + // DANGER ZONE BEGIN: exceptions will leave v_h in an invalid state. + v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder Holder temp_holder(std::move(v_h.holder>())); // Steal the holder v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null v_h.set_instance_registered(false); + // DANGER ZONE END. construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(*ptr)); } else { @@ -153,7 +157,9 @@ void construct(value_and_holder &v_h, Alias *alias_ptr, bool) { // holder. This also handles types like std::shared_ptr and std::unique_ptr where T is a // derived type (through those holder's implicit conversion from derived class holder // constructors). -template +template >::value, int> + = 0> void construct(value_and_holder &v_h, Holder holder, bool need_alias) { PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); auto *ptr = holder_helper>::get(holder); @@ -195,6 +201,73 @@ void construct(value_and_holder &v_h, Alias &&result, bool) { v_h.value_ptr() = new Alias(std::move(result)); } +template >, + detail::enable_if_t>::value, int> + = 0> +void construct(value_and_holder &v_h, std::unique_ptr, D> &&unq_ptr, bool need_alias) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); + auto *ptr = unq_ptr.get(); + no_nullptr(ptr); + if (Class::has_alias && need_alias && !is_alias(ptr)) { + throw type_error("pybind11::init(): construction failed: returned std::unique_ptr pointee " + "is not an alias instance"); + } + // Here and below: if the new object is a trampoline, the shared_from_this mechanism needs + // to be prevented from accessing the smart_holder vptr, because it does not keep the + // trampoline Python object alive. For types that don't inherit from enable_shared_from_this + // it does not matter if void_cast_raw_ptr is true or false, therefore it's not necessary + // to also inspect the type. + auto smhldr = type_caster>::smart_holder_from_unique_ptr( + std::move(unq_ptr), /*void_cast_raw_ptr*/ Class::has_alias && is_alias(ptr)); + v_h.value_ptr() = ptr; + v_h.type->init_instance(v_h.inst, &smhldr); +} + +template >, + detail::enable_if_t>::value, int> + = 0> +void construct(value_and_holder &v_h, + std::unique_ptr, D> &&unq_ptr, + bool /*need_alias*/) { + auto *ptr = unq_ptr.get(); + no_nullptr(ptr); + auto smhldr = type_caster>::smart_holder_from_unique_ptr( + std::move(unq_ptr), /*void_cast_raw_ptr*/ true); + v_h.value_ptr() = ptr; + v_h.type->init_instance(v_h.inst, &smhldr); +} + +template >::value, int> + = 0> +void construct(value_and_holder &v_h, std::shared_ptr> &&shd_ptr, bool need_alias) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); + auto *ptr = shd_ptr.get(); + no_nullptr(ptr); + if (Class::has_alias && need_alias && !is_alias(ptr)) { + throw type_error("pybind11::init(): construction failed: returned std::shared_ptr pointee " + "is not an alias instance"); + } + auto smhldr = type_caster>::smart_holder_from_shared_ptr(shd_ptr); + v_h.value_ptr() = ptr; + v_h.type->init_instance(v_h.inst, &smhldr); +} + +template >::value, int> + = 0> +void construct(value_and_holder &v_h, + std::shared_ptr> &&shd_ptr, + bool /*need_alias*/) { + auto *ptr = shd_ptr.get(); + no_nullptr(ptr); + auto smhldr = type_caster>::smart_holder_from_shared_ptr(shd_ptr); + v_h.value_ptr() = ptr; + v_h.type->init_instance(v_h.inst, &smhldr); +} + // Implementing class for py::init<...>() template struct constructor {