Using dynamic_cast<AliasType> to determine pointee_depends_on_holder_owner. (#2910)

* Adaption of PyCLIF virtual_py_cpp_mix test.

* Removing ValueError: Ownership of instance with virtual overrides in Python cannot be transferred to C++. TODO: static_assert alias class needs to inherit from virtual_overrider_self_life_support.

* Bringing back ValueError: "... instance cannot safely be transferred to C++.", but based on dynamic_cast<AliasType>.

* Fixing oversight: adding test_class_sh_virtual_py_cpp_mix.cpp to cmake file.

* clang <= 3.6 compatibility.

* Fixing oversight: dynamic_raw_ptr_cast_if_possible needs special handling for To = void. Adding corresponding missing test in test_class_sh_virtual_py_cpp_mix. Moving dynamic_raw_ptr_cast_if_possible to separate header.

* Changing py::detail::virtual_overrider_self_life_support to py::virtual_overrider_self_life_support.
This commit is contained in:
Ralf W. Grosse-Kunstleve
2021-03-19 12:18:39 -07:00
committed by GitHub
parent 3f35af7441
commit 5319ca3817
12 changed files with 207 additions and 52 deletions

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2021 The Pybind Development Team.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#pragma once
#include "common.h"
#include <type_traits>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
template <typename To, typename From, typename SFINAE = void>
struct dynamic_raw_ptr_cast_is_possible : std::false_type {};
template <typename To, typename From>
struct dynamic_raw_ptr_cast_is_possible<
To,
From,
detail::enable_if_t<!std::is_same<To, void>::value && std::is_polymorphic<From>::value>>
: std::true_type {};
template <typename To,
typename From,
detail::enable_if_t<!dynamic_raw_ptr_cast_is_possible<To, From>::value, int> = 0>
To *dynamic_raw_ptr_cast_if_possible(From * /*ptr*/) {
return nullptr;
}
template <typename To,
typename From,
detail::enable_if_t<dynamic_raw_ptr_cast_is_possible<To, From>::value, int> = 0>
To *dynamic_raw_ptr_cast_if_possible(From *ptr) {
return dynamic_cast<To *>(ptr);
}
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@@ -6,14 +6,15 @@
#include "../gil.h"
#include "../pytypes.h"
#include "../virtual_overrider_self_life_support.h"
#include "common.h"
#include "descr.h"
#include "dynamic_raw_ptr_cast_if_possible.h"
#include "internals.h"
#include "smart_holder_poc.h"
#include "smart_holder_sfinae_hooks_only.h"
#include "type_caster_base.h"
#include "typeid.h"
#include "virtual_overrider_self_life_support.h"
#include <cstddef>
#include <memory>
@@ -263,15 +264,13 @@ struct smart_holder_type_caster_class_hooks : smart_holder_type_caster_base_tag
return &modified_type_caster_generic_load_impl::local_load;
}
template <typename T>
static void init_instance_for_type(detail::instance *inst,
const void *holder_const_void_ptr,
bool has_alias) {
template <typename WrappedType, typename AliasType>
static void init_instance_for_type(detail::instance *inst, const void *holder_const_void_ptr) {
// Need for const_cast is a consequence of the type_info::init_instance type:
// void (*init_instance)(instance *, const void *);
auto holder_void_ptr = const_cast<void *>(holder_const_void_ptr);
auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(T)));
auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(WrappedType)));
if (!v_h.instance_registered()) {
register_instance(inst, v_h.value_ptr(), v_h.type);
v_h.set_instance_registered();
@@ -282,13 +281,14 @@ struct smart_holder_type_caster_class_hooks : smart_holder_type_caster_base_tag
auto holder_ptr = static_cast<holder_type *>(holder_void_ptr);
new (std::addressof(v_h.holder<holder_type>())) holder_type(std::move(*holder_ptr));
} else if (inst->owned) {
new (std::addressof(v_h.holder<holder_type>()))
holder_type(holder_type::from_raw_ptr_take_ownership(v_h.value_ptr<T>()));
new (std::addressof(v_h.holder<holder_type>())) holder_type(
holder_type::from_raw_ptr_take_ownership(v_h.value_ptr<WrappedType>()));
} else {
new (std::addressof(v_h.holder<holder_type>()))
holder_type(holder_type::from_raw_ptr_unowned(v_h.value_ptr<T>()));
holder_type(holder_type::from_raw_ptr_unowned(v_h.value_ptr<WrappedType>()));
}
v_h.holder<holder_type>().pointee_depends_on_holder_owner = has_alias;
v_h.holder<holder_type>().pointee_depends_on_holder_owner
= dynamic_raw_ptr_cast_if_possible<AliasType>(v_h.value_ptr<WrappedType>()) != nullptr;
v_h.set_holder_constructed();
}
@@ -391,10 +391,11 @@ struct smart_holder_type_caster_load {
T *raw_type_ptr = convert_type(raw_void_ptr);
auto *self_life_support
= dynamic_cast_virtual_overrider_self_life_support_ptr(raw_type_ptr);
= dynamic_raw_ptr_cast_if_possible<virtual_overrider_self_life_support>(raw_type_ptr);
if (self_life_support == nullptr && holder().pointee_depends_on_holder_owner) {
throw value_error("Ownership of instance with virtual overrides in Python cannot be "
"transferred to C++.");
throw value_error("Alias class (also known as trampoline) does not inherit from "
"py::virtual_overrider_self_life_support, therefore the "
"ownership of this instance cannot safely be transferred to C++.");
}
// Critical transfer-of-ownership section. This must stay together.

View File

@@ -1612,9 +1612,10 @@ private:
// clang-format on
template <typename T = type,
typename A = type_alias,
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<T>::value, int> = 0>
static void init_instance(detail::instance *inst, const void *holder_ptr) {
detail::type_caster<T>::template init_instance_for_type<type>(inst, holder_ptr, has_alias);
detail::type_caster<T>::template init_instance_for_type<T, A>(inst, holder_ptr);
}
// clang-format off

View File

@@ -4,24 +4,23 @@
#pragma once
#include "common.h"
#include "smart_holder_poc.h"
#include "type_caster_base.h"
#include <type_traits>
#include "detail/common.h"
#include "detail/smart_holder_poc.h"
#include "detail/type_caster_base.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
PYBIND11_NAMESPACE_BEGIN(detail)
// SMART_HOLDER_WIP: Needs refactoring of existing pybind11 code.
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo);
PYBIND11_NAMESPACE_END(detail)
// The original core idea for this struct goes back to PyCLIF:
// https://github.com/google/clif/blob/07f95d7e69dca2fcf7022978a55ef3acff506c19/clif/python/runtime.cc#L37
// URL provided here mainly to give proper credit. To fully explain the `HoldPyObj` feature, more
// context is needed (SMART_HOLDER_WIP).
struct virtual_overrider_self_life_support {
value_and_holder loaded_v_h;
detail::value_and_holder loaded_v_h;
~virtual_overrider_self_life_support() {
if (loaded_v_h.inst != nullptr && loaded_v_h.vh != nullptr) {
void *value_void_ptr = loaded_v_h.value_ptr();
@@ -30,7 +29,7 @@ struct virtual_overrider_self_life_support {
Py_DECREF((PyObject *) loaded_v_h.inst);
loaded_v_h.value_ptr() = nullptr;
loaded_v_h.holder<pybindit::memory::smart_holder>().release_disowned();
deregister_instance(loaded_v_h.inst, value_void_ptr, loaded_v_h.type);
detail::deregister_instance(loaded_v_h.inst, value_void_ptr, loaded_v_h.type);
PyGILState_Release(threadstate);
}
}
@@ -46,17 +45,4 @@ struct virtual_overrider_self_life_support {
= default;
};
template <typename T, detail::enable_if_t<!std::is_polymorphic<T>::value, int> = 0>
virtual_overrider_self_life_support *
dynamic_cast_virtual_overrider_self_life_support_ptr(T * /*raw_type_ptr*/) {
return nullptr;
}
template <typename T, detail::enable_if_t<std::is_polymorphic<T>::value, int> = 0>
virtual_overrider_self_life_support *
dynamic_cast_virtual_overrider_self_life_support_ptr(T *raw_type_ptr) {
return dynamic_cast<virtual_overrider_self_life_support *>(raw_type_ptr);
}
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)