Commit d3b2d567 by Ralf W. Grosse-Kunstleve

Merge branch 'test_unique_ptr_member' into pr2672_use_smart_holder_as_default

parents dc10e8a9 6005632b
......@@ -105,7 +105,9 @@ set(PYBIND11_HEADERS
include/pybind11/detail/descr.h
include/pybind11/detail/init.h
include/pybind11/detail/internals.h
include/pybind11/detail/smart_holder_init_inline_include.h
include/pybind11/detail/smart_holder_poc.h
include/pybind11/detail/smart_holder_type_casters_inline_include.h
include/pybind11/detail/typeid.h
include/pybind11/attr.h
include/pybind11/buffer_info.h
......
// clang-format off
/*
pybind11/cast.h: Partial template specializations to cast between
C++ and Python types
......@@ -18,7 +19,6 @@
#include "detail/typeid.h"
#include "detail/descr.h"
#include "detail/internals.h"
#include "detail/smart_holder_poc.h"
#include <array>
#include <limits>
#include <tuple>
......@@ -42,9 +42,6 @@
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
using pybindit::memory::smart_holder;
PYBIND11_NAMESPACE_BEGIN(detail)
/// A life support system for temporary objects created by `type_caster::load()`.
......@@ -959,713 +956,49 @@ protected:
static Constructor make_move_constructor(...) { return nullptr; }
};
// clang-format on
// Tag to be used as base class, inspected by is_smart_holder_type_caster<T> test.
struct is_smart_holder_type_caster_base_tag {};
template <typename T>
struct is_smart_holder_type_caster;
//DETAIL/SMART_HOLDER_TYPE_CASTERS_H/BEGIN/////////////////////////////////////////////////////////
//FWD begin
inline void register_instance(instance *self, void *valptr, const type_info *tinfo);
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo);
//FWD end
// The modified_type_caster_generic_load_impl could replace type_caster_generic::load_impl but not
// vice versa. The main difference is that the original code only propagates a reference to the
// held value, while the modified implementation propagates value_and_holder.
// clang-format off
class modified_type_caster_generic_load_impl {
public:
PYBIND11_NOINLINE modified_type_caster_generic_load_impl(const std::type_info &type_info)
: typeinfo(get_type_info(type_info)), cpptype(&type_info) { }
explicit modified_type_caster_generic_load_impl(const type_info *typeinfo = nullptr)
: typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { }
bool load(handle src, bool convert) {
return load_impl<modified_type_caster_generic_load_impl>(src, convert);
}
// Base methods for generic caster; there are overridden in copyable_holder_caster
void load_value_and_holder(value_and_holder &&v_h) {
if (!v_h.holder_constructed()) {
// This is needed for old-style __init__.
// type_caster_generic::load_value BEGIN
auto *&vptr = v_h.value_ptr();
// Lazy allocation for unallocated values:
if (vptr == nullptr) {
// Lazy allocation for unallocated values:
auto *type = v_h.type ? v_h.type : typeinfo;
if (type->operator_new) {
vptr = type->operator_new(type->type_size);
} else {
#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
vptr = ::operator new(type->type_size,
std::align_val_t(type->type_align));
else
#endif
vptr = ::operator new(type->type_size);
}
}
// type_caster_generic::load_value END
}
loaded_v_h = std::move(v_h);
loaded_v_h.type = typeinfo;
}
bool try_implicit_casts(handle src, bool convert) {
for (auto &cast : typeinfo->implicit_casts) {
modified_type_caster_generic_load_impl sub_caster(*cast.first);
if (sub_caster.load(src, convert)) {
if (loaded_v_h_cpptype != nullptr) {
pybind11_fail("smart_holder_type_casters: try_implicit_casts failure.");
}
loaded_v_h = sub_caster.loaded_v_h;
loaded_v_h_cpptype = cast.first;
implicit_cast = cast.second;
return true;
}
}
return false;
}
bool try_direct_conversions(handle src) {
for (auto &converter : *typeinfo->direct_conversions) {
if (converter(src.ptr(), unowned_void_ptr_from_direct_conversion)) {
return true;
}
}
return false;
}
PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) {
std::unique_ptr<modified_type_caster_generic_load_impl> loader(
new modified_type_caster_generic_load_impl(ti));
if (loader->load(src, false)) {
// Trick to work with the existing pybind11 internals.
// The void pointer is immediately captured in a new unique_ptr in
// try_load_foreign_module_local. If this assumption is violated sanitizers
// will most likely flag a leak (verified to be the case with ASAN).
return static_cast<void *>(loader.release());
}
return nullptr;
}
/// Try to load with foreign typeinfo, if available. Used when there is no
/// native typeinfo, or when the native one wasn't able to produce a value.
PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) {
constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID;
const auto pytype = type::handle_of(src);
if (!hasattr(pytype, local_key))
return false;
type_info *foreign_typeinfo = reinterpret_borrow<capsule>(getattr(pytype, local_key));
// Only consider this foreign loader if actually foreign and is a loader of the correct cpp type
if (foreign_typeinfo->module_local_load == &local_load
|| (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype)))
return false;
void* foreign_loader_void_ptr =
foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo);
if (foreign_loader_void_ptr != nullptr) {
auto foreign_loader = std::unique_ptr<modified_type_caster_generic_load_impl>(
static_cast<modified_type_caster_generic_load_impl *>(foreign_loader_void_ptr));
// Magic number intentionally hard-coded for simplicity and maximum robustness.
if (foreign_loader->local_load_safety_guard != 1887406645) {
pybind11_fail(
"smart_holder_type_casters: Unexpected local_load_safety_guard,"
" possibly due to py::class_ holder mixup.");
}
if (loaded_v_h_cpptype != nullptr) {
pybind11_fail("smart_holder_type_casters: try_load_foreign_module_local failure.");
}
loaded_v_h = foreign_loader->loaded_v_h;
loaded_v_h_cpptype = foreign_loader->loaded_v_h_cpptype;
implicit_cast = foreign_loader->implicit_cast;
return true;
}
return false;
}
// Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant
// bits of code between here and copyable_holder_caster where the two classes need different
// logic (without having to resort to virtual inheritance).
template <typename ThisT>
PYBIND11_NOINLINE bool load_impl(handle src, bool convert) {
if (!src) return false;
if (!typeinfo) return try_load_foreign_module_local(src);
if (src.is_none()) {
// Defer accepting None to other overloads (if we aren't in convert mode):
if (!convert) return false;
loaded_v_h = value_and_holder();
return true;
}
auto &this_ = static_cast<ThisT &>(*this);
PyTypeObject *srctype = Py_TYPE(src.ptr());
// Case 1: If src is an exact type match for the target type then we can reinterpret_cast
// the instance's value pointer to the target type:
if (srctype == typeinfo->type) {
this_.load_value_and_holder(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
return true;
}
// Case 2: We have a derived class
else if (PyType_IsSubtype(srctype, typeinfo->type)) {
auto &bases = all_type_info(srctype); // subtype bases
bool no_cpp_mi = typeinfo->simple_type;
// Case 2a: the python type is a Python-inherited derived class that inherits from just
// one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of
// the right type and we can use reinterpret_cast.
// (This is essentially the same as case 2b, but because not using multiple inheritance
// is extremely common, we handle it specially to avoid the loop iterator and type
// pointer lookup overhead)
if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) {
this_.load_value_and_holder(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
loaded_v_h_cpptype = bases.front()->cpptype;
reinterpret_cast_deemed_ok = true;
return true;
}
// Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if
// we can find an exact match (or, for a simple C++ type, an inherited match); if so, we
// can safely reinterpret_cast to the relevant pointer.
else if (bases.size() > 1) {
for (auto base : bases) {
if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) {
this_.load_value_and_holder(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base));
loaded_v_h_cpptype = base->cpptype;
reinterpret_cast_deemed_ok = true;
return true;
}
}
}
// Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match
// in the registered bases, above, so try implicit casting (needed for proper C++ casting
// when MI is involved).
if (this_.try_implicit_casts(src, convert)) {
return true;
}
}
// Perform an implicit conversion
if (convert) {
for (auto &converter : typeinfo->implicit_conversions) {
auto temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
if (load_impl<ThisT>(temp, false)) {
loader_life_support::add_patient(temp);
return true;
}
}
if (this_.try_direct_conversions(src))
return true;
}
// Failed to match local typeinfo. Try again with global.
if (typeinfo->module_local) {
if (auto gtype = get_global_type_info(*typeinfo->cpptype)) {
typeinfo = gtype;
return load(src, false);
}
}
// Global typeinfo has precedence over foreign module_local
return try_load_foreign_module_local(src);
}
const type_info *typeinfo = nullptr;
const std::type_info *cpptype = nullptr;
void *unowned_void_ptr_from_direct_conversion = nullptr;
const std::type_info *loaded_v_h_cpptype = nullptr;
void *(*implicit_cast)(void *) = nullptr;
value_and_holder loaded_v_h;
bool reinterpret_cast_deemed_ok = false;
// Magic number intentionally hard-coded, to guard against class_ holder mixups.
// Ideally type_caster_generic would have a similar guard, but this requires a change there.
std::size_t local_load_safety_guard = 1887406645; // 32-bit compatible value for portability.
};
// clang-format on
struct smart_holder_type_caster_class_hooks : is_smart_holder_type_caster_base_tag {
static decltype(&modified_type_caster_generic_load_impl::local_load)
get_local_load_function_ptr() {
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) {
// 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)));
if (!v_h.instance_registered()) {
register_instance(inst, v_h.value_ptr(), v_h.type);
v_h.set_instance_registered();
}
using holder_type = pybindit::memory::smart_holder;
if (holder_void_ptr) {
// Note: inst->owned ignored.
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>()));
} else {
new (std::addressof(v_h.holder<holder_type>()))
holder_type(holder_type::from_raw_ptr_unowned(v_h.value_ptr<T>()));
}
v_h.set_holder_constructed();
}
};
template <typename T>
struct smart_holder_type_caster_load {
using holder_type = pybindit::memory::smart_holder;
bool load(handle src, bool convert) {
static_assert(is_smart_holder_type_caster<T>::value, "Internal consistency error.");
load_impl = modified_type_caster_generic_load_impl(typeid(T));
if (!load_impl.load(src, convert))
return false;
return true;
}
T *loaded_as_raw_ptr_unowned() const {
void *void_ptr = load_impl.unowned_void_ptr_from_direct_conversion;
if (void_ptr == nullptr) {
if (have_holder()) {
throw_if_uninitialized_or_disowned_holder();
void_ptr = holder().template as_raw_ptr_unowned<void>();
} else if (load_impl.loaded_v_h.vh != nullptr)
void_ptr = load_impl.loaded_v_h.value_ptr();
if (void_ptr == nullptr)
return nullptr;
}
return convert_type(void_ptr);
}
T &loaded_as_lvalue_ref() const {
T *raw_ptr = loaded_as_raw_ptr_unowned();
if (raw_ptr == nullptr) throw reference_cast_error();
return *raw_ptr;
}
std::shared_ptr<T> loaded_as_shared_ptr() const {
if (load_impl.unowned_void_ptr_from_direct_conversion != nullptr)
throw cast_error("Unowned pointer from direct conversion cannot be converted to a"
" std::shared_ptr.");
if (!have_holder()) return nullptr;
throw_if_uninitialized_or_disowned_holder();
std::shared_ptr<void> void_ptr = holder().template as_shared_ptr<void>();
return std::shared_ptr<T>(void_ptr, convert_type(void_ptr.get()));
}
template <typename D>
std::unique_ptr<T, D> loaded_as_unique_ptr(const char *context = "loaded_as_unique_ptr") {
if (load_impl.unowned_void_ptr_from_direct_conversion != nullptr)
throw cast_error("Unowned pointer from direct conversion cannot be converted to a"
" std::unique_ptr.");
if (!have_holder()) return nullptr;
throw_if_uninitialized_or_disowned_holder();
holder().template ensure_compatible_rtti_uqp_del<T, D>(context);
holder().ensure_use_count_1(context);
auto raw_void_ptr = holder().template as_raw_ptr_unowned<void>();
// SMART_HOLDER_WIP: MISSING: Safety checks for type conversions
// (T must be polymorphic or meet certain other conditions).
T *raw_type_ptr = convert_type(raw_void_ptr);
// Critical transfer-of-ownership section. This must stay together.
holder().release_ownership();
auto result = std::unique_ptr<T, D>(raw_type_ptr);
void *value_void_ptr = load_impl.loaded_v_h.value_ptr();
if (value_void_ptr != raw_void_ptr) {
pybind11_fail("smart_holder_type_casters: loaded_as_unique_ptr failure:"
" value_void_ptr != raw_void_ptr");
}
load_impl.loaded_v_h.value_ptr() = nullptr;
deregister_instance(load_impl.loaded_v_h.inst, value_void_ptr, load_impl.loaded_v_h.type);
return result;
}
private:
modified_type_caster_generic_load_impl load_impl;
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
bool have_holder() const {
return load_impl.loaded_v_h.vh != nullptr && load_impl.loaded_v_h.holder_constructed();
}
// SMART_HOLDER_WIP: Needs refactoring of existing pybind11 code.
#define PYBIND11_CAST_H_SMART_HOLDER_TYPE_CASTERS_INLINE_INCLUDE_SAFETY_GUARD
#include "detail/smart_holder_type_casters_inline_include.h"
#undef PYBIND11_CAST_H_SMART_HOLDER_TYPE_CASTERS_INLINE_INCLUDE_SAFETY_GUARD
holder_type &holder() const { return load_impl.loaded_v_h.holder<holder_type>(); }
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
// have_holder() must be true or this function will fail.
void throw_if_uninitialized_or_disowned_holder() const {
if (!holder().is_populated) {
pybind11_fail("Missing value for wrapped C++ type:"
" Python instance is uninitialized.");
}
if (!holder().has_pointee()) {
throw cast_error("Missing value for wrapped C++ type:"
" Python instance was disowned.");
}
}
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
T *convert_type(void *void_ptr) const {
if (void_ptr != nullptr && load_impl.loaded_v_h_cpptype != nullptr
&& !load_impl.reinterpret_cast_deemed_ok && load_impl.implicit_cast != nullptr) {
void_ptr = load_impl.implicit_cast(void_ptr);
}
return static_cast<T *>(void_ptr);
}
};
// SMART_HOLDER_WIP: IMPROVABLE: Formally factor out of type_caster_base.
struct make_constructor : private type_caster_base<int> { // Any type, nothing special about int.
using type_caster_base<int>::Constructor;
using type_caster_base<int>::make_copy_constructor;
using type_caster_base<int>::make_move_constructor;
};
# define PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS(T, ...)
template <typename T>
struct smart_holder_type_caster : smart_holder_type_caster_load<T>,
smart_holder_type_caster_class_hooks {
static constexpr auto name = _<T>();
// static handle cast(T, ...)
// is redundant (leads to ambiguous overloads).
static handle cast(T &&src, return_value_policy /*policy*/, handle parent) {
// type_caster_base BEGIN
// clang-format off
return cast(&src, return_value_policy::move, parent);
// clang-format on
// type_caster_base END
}
static handle cast(T const &src, return_value_policy policy, handle parent) {
// type_caster_base BEGIN
// clang-format off
if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference)
policy = return_value_policy::copy;
return cast(&src, policy, parent);
// clang-format on
// type_caster_base END
}
static handle cast(T &src, return_value_policy policy, handle parent) {
return cast(const_cast<T const &>(src), policy, parent); // Mutbl2Const
}
static handle cast(T const *src, return_value_policy policy, handle parent) {
auto st = type_caster_base<T>::src_and_type(src);
return cast_const_raw_ptr( // Originally type_caster_generic::cast.
st.first,
policy,
parent,
st.second,
make_constructor::make_copy_constructor(src),
make_constructor::make_move_constructor(src));
}
static handle cast(T *src, return_value_policy policy, handle parent) {
return cast(const_cast<T const *>(src), policy, parent); // Mutbl2Const
}
#if defined(_MSC_VER) && _MSC_VER < 1910
// Working around MSVC 2015 bug. const-correctness is lost.
// SMART_HOLDER_WIP: IMPROVABLE: make common code work with MSVC 2015.
template <typename T_>
using cast_op_type = detail::cast_op_type<T_>;
#else
template <typename T_>
using cast_op_type = conditional_t<
std::is_same<remove_reference_t<T_>, T const *>::value,
T const *,
conditional_t<std::is_same<remove_reference_t<T_>, T *>::value,
T *,
conditional_t<std::is_same<T_, T const &>::value, T const &, T &>>>;
#endif
class type_caster_for_class_ : public type_caster_base<T> {};
// The const operators here prove that the existing type_caster mechanism already supports
// const-correctness. However, fully implementing const-correctness inside this type_caster
// is still a major project.
operator T const &() const {
return const_cast<smart_holder_type_caster *>(this)->loaded_as_lvalue_ref();
}
operator T const *() const {
return const_cast<smart_holder_type_caster *>(this)->loaded_as_raw_ptr_unowned();
}
operator T &() { return this->loaded_as_lvalue_ref(); }
operator T *() { return this->loaded_as_raw_ptr_unowned(); }
// Originally type_caster_generic::cast.
PYBIND11_NOINLINE static handle cast_const_raw_ptr(const void *_src,
return_value_policy policy,
handle parent,
const detail::type_info *tinfo,
void *(*copy_constructor)(const void *),
void *(*move_constructor)(const void *),
const void *existing_holder = nullptr) {
if (!tinfo) // no type info: error will be set already
return handle();
void *src = const_cast<void *>(_src);
if (src == nullptr)
return none().release();
if (handle existing_inst = find_registered_python_instance(src, tinfo))
return existing_inst;
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
auto wrapper = reinterpret_cast<instance *>(inst.ptr());
wrapper->owned = false;
void *&valueptr = values_and_holders(wrapper).begin()->value_ptr();
switch (policy) {
case return_value_policy::automatic:
case return_value_policy::take_ownership:
valueptr = src;
wrapper->owned = true;
break;
case return_value_policy::automatic_reference:
case return_value_policy::reference:
valueptr = src;
wrapper->owned = false;
break;
case return_value_policy::copy:
if (copy_constructor)
valueptr = copy_constructor(src);
else {
#if defined(NDEBUG)
throw cast_error("return_value_policy = copy, but type is "
"non-copyable! (compile in debug mode for details)");
#else
std::string type_name(tinfo->cpptype->name());
detail::clean_type_id(type_name);
throw cast_error("return_value_policy = copy, but type " + type_name
+ " is non-copyable!");
#endif
}
wrapper->owned = true;
break;
case return_value_policy::move:
if (move_constructor)
valueptr = move_constructor(src);
else if (copy_constructor)
valueptr = copy_constructor(src);
else {
#if defined(NDEBUG)
throw cast_error("return_value_policy = move, but type is neither "
"movable nor copyable! "
"(compile in debug mode for details)");
#else
std::string type_name(tinfo->cpptype->name());
detail::clean_type_id(type_name);
throw cast_error("return_value_policy = move, but type " + type_name
+ " is neither movable nor copyable!");
#endif
}
wrapper->owned = true;
break;
case return_value_policy::reference_internal:
valueptr = src;
wrapper->owned = false;
keep_alive_impl(inst, parent);
break;
default:
throw cast_error("unhandled return_value_policy: should not happen!");
# define PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS(T, ...) \
namespace pybind11 { \
namespace detail { \
template <> \
class type_caster<T> : public type_caster_base<T> {}; \
template <> \
class type_caster<__VA_ARGS__> : public type_caster_holder<T, __VA_ARGS__> {}; \
} \
}
tinfo->init_instance(wrapper, existing_holder);
return inst.release();
}
};
template <typename T>
struct smart_holder_type_caster<std::shared_ptr<T>> : smart_holder_type_caster_load<T>,
smart_holder_type_caster_class_hooks {
static constexpr auto name = _<std::shared_ptr<T>>();
static handle cast(const std::shared_ptr<T> &src, return_value_policy policy, handle parent) {
if (policy != return_value_policy::automatic
&& policy != return_value_policy::reference_internal) {
// SMART_HOLDER_WIP: IMPROVABLE: Error message.
throw cast_error("Invalid return_value_policy for shared_ptr.");
}
auto src_raw_ptr = src.get();
auto st = type_caster_base<T>::src_and_type(src_raw_ptr);
if (st.first == nullptr)
return none().release(); // PyErr was set already.
void *src_raw_void_ptr = static_cast<void *>(src_raw_ptr);
const detail::type_info *tinfo = st.second;
if (handle existing_inst = find_registered_python_instance(src_raw_void_ptr, tinfo))
// SMART_HOLDER_WIP: MISSING: Enforcement of consistency with existing smart_holder.
// SMART_HOLDER_WIP: MISSING: keep_alive.
return existing_inst;
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
auto *inst_raw_ptr = reinterpret_cast<instance *>(inst.ptr());
inst_raw_ptr->owned = true;
void *&valueptr = values_and_holders(inst_raw_ptr).begin()->value_ptr();
valueptr = src_raw_void_ptr;
auto smhldr = pybindit::memory::smart_holder::from_shared_ptr(src);
tinfo->init_instance(inst_raw_ptr, static_cast<const void *>(&smhldr));
if (policy == return_value_policy::reference_internal)
keep_alive_impl(inst, parent);
return inst.release();
}
template <typename>
using cast_op_type = std::shared_ptr<T>;
operator std::shared_ptr<T>() { return this->loaded_as_shared_ptr(); }
};
class type_caster_for_class_ : public smart_holder_type_caster<T> {};
template <typename T>
struct smart_holder_type_caster<std::shared_ptr<T const>> : smart_holder_type_caster_load<T>,
smart_holder_type_caster_class_hooks {
static constexpr auto name = _<std::shared_ptr<T const>>();
static handle
cast(const std::shared_ptr<T const> &src, return_value_policy policy, handle parent) {
return smart_holder_type_caster<std::shared_ptr<T>>::cast(
std::const_pointer_cast<T>(src), // Const2Mutbl
policy,
parent);
}
template <typename>
using cast_op_type = std::shared_ptr<T const>;
operator std::shared_ptr<T const>() { return this->loaded_as_shared_ptr(); } // Mutbl2Const
};
template <typename T, typename D>
struct smart_holder_type_caster<std::unique_ptr<T, D>> : smart_holder_type_caster_load<T>,
smart_holder_type_caster_class_hooks {
static constexpr auto name = _<std::unique_ptr<T, D>>();
static handle cast(std::unique_ptr<T, D> &&src, return_value_policy policy, handle parent) {
if (policy != return_value_policy::automatic
&& policy != return_value_policy::reference_internal
&& policy != return_value_policy::move) {
// SMART_HOLDER_WIP: IMPROVABLE: Error message.
throw cast_error("Invalid return_value_policy for unique_ptr.");
}
auto src_raw_ptr = src.get();
auto st = type_caster_base<T>::src_and_type(src_raw_ptr);
if (st.first == nullptr)
return none().release(); // PyErr was set already.
void *src_raw_void_ptr = static_cast<void *>(src_raw_ptr);
const detail::type_info *tinfo = st.second;
if (find_registered_python_instance(src_raw_void_ptr, tinfo))
throw cast_error("Invalid unique_ptr: another instance owns this pointer already.");
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
auto *inst_raw_ptr = reinterpret_cast<instance *>(inst.ptr());
inst_raw_ptr->owned = true;
void *&valueptr = values_and_holders(inst_raw_ptr).begin()->value_ptr();
valueptr = src_raw_void_ptr;
auto smhldr = pybindit::memory::smart_holder::from_unique_ptr(std::move(src));
tinfo->init_instance(inst_raw_ptr, static_cast<const void *>(&smhldr));
if (policy == return_value_policy::reference_internal)
keep_alive_impl(inst, parent);
return inst.release();
}
template <typename>
using cast_op_type = std::unique_ptr<T, D>;
operator std::unique_ptr<T, D>() { return this->template loaded_as_unique_ptr<D>(); }
};
template <typename T, typename D>
struct smart_holder_type_caster<std::unique_ptr<T const, D>>
: smart_holder_type_caster_load<T>, smart_holder_type_caster_class_hooks {
static constexpr auto name = _<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
policy,
parent);
}
template <typename>
using cast_op_type = std::unique_ptr<T const, D>;
operator std::unique_ptr<T const, D>() { return this->template loaded_as_unique_ptr<D>(); }
};
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
#define PYBIND11_SMART_HOLDER_TYPE_CASTERS(T) \
namespace pybind11 { \
namespace detail { \
template <> \
class type_caster<T> : public smart_holder_type_caster<T> {}; \
template <> \
class type_caster<std::shared_ptr<T>> : public smart_holder_type_caster<std::shared_ptr<T>> { \
}; \
template <> \
class type_caster<std::shared_ptr<T const>> \
: public smart_holder_type_caster<std::shared_ptr<T const>> {}; \
template <typename D> \
class type_caster<std::unique_ptr<T, D>> \
: public smart_holder_type_caster<std::unique_ptr<T, D>> {}; \
template <typename D> \
class type_caster<std::unique_ptr<T const, D>> \
: public smart_holder_type_caster<std::unique_ptr<T const, D>> {}; \
} \
}
#endif
//DETAIL/SMART_HOLDER_TYPE_CASTERS_H/END///////////////////////////////////////////////////////////
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
#define PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS(T, ...)
template <typename T> class type_caster_for_class_ : public type_caster_base<T> {};
#else
#define PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS(T, ...) \
namespace pybind11 { \
namespace detail { \
template <> \
class type_caster<T> : public type_caster_base<T> {}; \
template <> \
class type_caster<__VA_ARGS__> : public type_caster_holder<T, __VA_ARGS__> {}; \
} \
}
template <typename T> class type_caster_for_class_ : public smart_holder_type_caster<T> {};
template <typename T>
class type_caster_for_class_<std::shared_ptr<T>> : public smart_holder_type_caster<std::shared_ptr<T>> {};
class type_caster_for_class_<std::shared_ptr<T>>
: public smart_holder_type_caster<std::shared_ptr<T>> {};
template <typename T>
class type_caster_for_class_<std::shared_ptr<T const>>
......@@ -1679,20 +1012,22 @@ template <typename T, typename D>
class type_caster_for_class_<std::unique_ptr<T const, D>>
: public smart_holder_type_caster<std::unique_ptr<T const, D>> {};
#define PYBIND11_SMART_HOLDER_TYPE_CASTERS(T)
# define PYBIND11_SMART_HOLDER_TYPE_CASTERS(T)
#endif
template <typename type, typename SFINAE = void> class type_caster : public type_caster_for_class_<type> { };
template <typename type, typename SFINAE = void>
class type_caster : public type_caster_for_class_<type> {};
template <typename type> using make_caster = type_caster<intrinsic_t<type>>;
template <typename type>
using make_caster = type_caster<intrinsic_t<type>>;
template <typename T>
struct is_smart_holder_type_caster {
static constexpr bool value = std::is_base_of<
is_smart_holder_type_caster_base_tag,
make_caster<T>>::value;
static constexpr bool value
= std::is_base_of<is_smart_holder_type_caster_base_tag, make_caster<T>>::value;
};
// clang-format off
// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T
template <typename T> typename make_caster<T>::template cast_op_type<T> cast_op(make_caster<T> &caster) {
......@@ -2468,8 +1803,8 @@ template <typename T> using move_never = none_of<move_always<T>, move_if_unrefer
template <typename type> using cast_is_temporary_value_reference = bool_constant<
(std::is_reference<type>::value || std::is_pointer<type>::value) &&
!std::is_base_of<type_caster_generic, make_caster<type>>::value &&
!std::is_same<intrinsic_t<type>, void>::value &&
!is_smart_holder_type_caster<intrinsic_t<type>>::value
!is_smart_holder_type_caster<intrinsic_t<type>>::value &&
!std::is_same<intrinsic_t<type>, void>::value
>;
// When a value returned from a C++ function is being cast back to Python, we almost always want to
......
......@@ -169,55 +169,10 @@ void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
v_h.value_ptr() = new Alias<Class>(std::move(result));
}
//DETAIL/SMART_HOLDER_INIT_H/BEGIN/////////////////////////////////////////////////////////////////
template <typename Class, typename D = std::default_delete<Cpp<Class>>,
detail::enable_if_t<detail::is_smart_holder_type_caster<Cpp<Class>>::value, int> = 0>
void construct(value_and_holder &v_h, std::unique_ptr<Cpp<Class>, D> &&unq_ptr, bool need_alias) {
auto *ptr = unq_ptr.get();
no_nullptr(ptr);
if (Class::has_alias && need_alias)
throw type_error("pybind11::init(): construction failed: returned std::unique_ptr pointee "
"is not an alias instance");
auto smhldr = smart_holder::from_unique_ptr(std::move(unq_ptr));
v_h.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr);
}
template <typename Class, typename D = std::default_delete<Alias<Class>>,
detail::enable_if_t<detail::is_smart_holder_type_caster<Alias<Class>>::value, int> = 0>
void construct(value_and_holder &v_h, std::unique_ptr<Alias<Class>, D> &&unq_ptr, bool /*need_alias*/) {
auto *ptr = unq_ptr.get();
no_nullptr(ptr);
auto smhldr = smart_holder::from_unique_ptr(std::move(unq_ptr));
v_h.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr);
}
template <typename Class,
detail::enable_if_t<detail::is_smart_holder_type_caster<Cpp<Class>>::value, int> = 0>
void construct(value_and_holder &v_h, std::shared_ptr<Cpp<Class>> &&shd_ptr, bool need_alias) {
auto *ptr = shd_ptr.get();
no_nullptr(ptr);
if (Class::has_alias && need_alias)
throw type_error("pybind11::init(): construction failed: returned std::shared_ptr pointee "
"is not an alias instance");
auto smhldr = smart_holder::from_shared_ptr(std::move(shd_ptr));
v_h.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr);
}
template <typename Class,
detail::enable_if_t<detail::is_smart_holder_type_caster<Alias<Class>>::value, int> = 0>
void construct(value_and_holder &v_h, std::shared_ptr<Alias<Class>> &&shd_ptr, bool /*need_alias*/) {
auto *ptr = shd_ptr.get();
no_nullptr(ptr);
auto smhldr = smart_holder::from_shared_ptr(std::move(shd_ptr));
v_h.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr);
}
//DETAIL/SMART_HOLDER_INIT_H/END///////////////////////////////////////////////////////////////////
// SMART_HOLDER_WIP: Needs refactoring of existing pybind11 code.
#define PYBIND11_DETAIL_INIT_H_SMART_HOLDER_INIT_INLINE_INCLUDE_SAFETY_GUARD
#include "smart_holder_init_inline_include.h"
#undef PYBIND11_DETAIL_INIT_H_SMART_HOLDER_INIT_INLINE_INCLUDE_SAFETY_GUARD
// Implementing class for py::init<...>()
template <typename... Args>
......
#ifndef PYBIND11_DETAIL_INIT_H_SMART_HOLDER_INIT_INLINE_INCLUDE_SAFETY_GUARD
# error "THIS FILE MUST ONLY BE INCLUDED FROM pybind11/detail/init.h"
#endif
template <typename Class,
typename D = std::default_delete<Cpp<Class>>,
detail::enable_if_t<detail::is_smart_holder_type_caster<Cpp<Class>>::value, int> = 0>
void construct(value_and_holder &v_h, std::unique_ptr<Cpp<Class>, D> &&unq_ptr, bool need_alias) {
auto *ptr = unq_ptr.get();
no_nullptr(ptr);
if (Class::has_alias && need_alias)
throw type_error("pybind11::init(): construction failed: returned std::unique_ptr pointee "
"is not an alias instance");
auto smhldr = smart_holder::from_unique_ptr(std::move(unq_ptr));
v_h.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr);
}
template <typename Class,
typename D = std::default_delete<Alias<Class>>,
detail::enable_if_t<detail::is_smart_holder_type_caster<Alias<Class>>::value, int> = 0>
void construct(value_and_holder &v_h,
std::unique_ptr<Alias<Class>, D> &&unq_ptr,
bool /*need_alias*/) {
auto *ptr = unq_ptr.get();
no_nullptr(ptr);
auto smhldr = smart_holder::from_unique_ptr(std::move(unq_ptr));
v_h.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr);
}
template <typename Class,
detail::enable_if_t<detail::is_smart_holder_type_caster<Cpp<Class>>::value, int> = 0>
void construct(value_and_holder &v_h, std::shared_ptr<Cpp<Class>> &&shd_ptr, bool need_alias) {
auto *ptr = shd_ptr.get();
no_nullptr(ptr);
if (Class::has_alias && need_alias)
throw type_error("pybind11::init(): construction failed: returned std::shared_ptr pointee "
"is not an alias instance");
auto smhldr = smart_holder::from_shared_ptr(std::move(shd_ptr));
v_h.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr);
}
template <typename Class,
detail::enable_if_t<detail::is_smart_holder_type_caster<Alias<Class>>::value, int> = 0>
void construct(value_and_holder &v_h,
std::shared_ptr<Alias<Class>> &&shd_ptr,
bool /*need_alias*/) {
auto *ptr = shd_ptr.get();
no_nullptr(ptr);
auto smhldr = smart_holder::from_shared_ptr(std::move(shd_ptr));
v_h.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr);
}
#ifndef PYBIND11_CAST_H_SMART_HOLDER_TYPE_CASTERS_INLINE_INCLUDE_SAFETY_GUARD
# error "THIS FILE MUST ONLY BE INCLUDED FROM pybind11/cast.h"
#endif
#include "smart_holder_poc.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
using pybindit::memory::smart_holder;
PYBIND11_NAMESPACE_BEGIN(detail)
// SMART_HOLDER_WIP: Needs refactoring of existing pybind11 code.
inline void register_instance(instance *self, void *valptr, const type_info *tinfo);
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo);
// The modified_type_caster_generic_load_impl could replace type_caster_generic::load_impl but not
// vice versa. The main difference is that the original code only propagates a reference to the
// held value, while the modified implementation propagates value_and_holder.
// clang-format off
class modified_type_caster_generic_load_impl {
public:
PYBIND11_NOINLINE modified_type_caster_generic_load_impl(const std::type_info &type_info)
: typeinfo(get_type_info(type_info)), cpptype(&type_info) { }
explicit modified_type_caster_generic_load_impl(const type_info *typeinfo = nullptr)
: typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { }
bool load(handle src, bool convert) {
return load_impl<modified_type_caster_generic_load_impl>(src, convert);
}
// Base methods for generic caster; there are overridden in copyable_holder_caster
void load_value_and_holder(value_and_holder &&v_h) {
if (!v_h.holder_constructed()) {
// This is needed for old-style __init__.
// type_caster_generic::load_value BEGIN
auto *&vptr = v_h.value_ptr();
// Lazy allocation for unallocated values:
if (vptr == nullptr) {
// Lazy allocation for unallocated values:
auto *type = v_h.type ? v_h.type : typeinfo;
if (type->operator_new) {
vptr = type->operator_new(type->type_size);
} else {
#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
vptr = ::operator new(type->type_size,
std::align_val_t(type->type_align));
else
#endif
vptr = ::operator new(type->type_size);
}
}
// type_caster_generic::load_value END
}
loaded_v_h = std::move(v_h);
loaded_v_h.type = typeinfo;
}
bool try_implicit_casts(handle src, bool convert) {
for (auto &cast : typeinfo->implicit_casts) {
modified_type_caster_generic_load_impl sub_caster(*cast.first);
if (sub_caster.load(src, convert)) {
if (loaded_v_h_cpptype != nullptr) {
pybind11_fail("smart_holder_type_casters: try_implicit_casts failure.");
}
loaded_v_h = sub_caster.loaded_v_h;
loaded_v_h_cpptype = cast.first;
implicit_cast = cast.second;
return true;
}
}
return false;
}
bool try_direct_conversions(handle src) {
for (auto &converter : *typeinfo->direct_conversions) {
if (converter(src.ptr(), unowned_void_ptr_from_direct_conversion)) {
return true;
}
}
return false;
}
PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) {
std::unique_ptr<modified_type_caster_generic_load_impl> loader(
new modified_type_caster_generic_load_impl(ti));
if (loader->load(src, false)) {
// Trick to work with the existing pybind11 internals.
// The void pointer is immediately captured in a new unique_ptr in
// try_load_foreign_module_local. If this assumption is violated sanitizers
// will most likely flag a leak (verified to be the case with ASAN).
return static_cast<void *>(loader.release());
}
return nullptr;
}
/// Try to load with foreign typeinfo, if available. Used when there is no
/// native typeinfo, or when the native one wasn't able to produce a value.
PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) {
constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID;
const auto pytype = type::handle_of(src);
if (!hasattr(pytype, local_key))
return false;
type_info *foreign_typeinfo = reinterpret_borrow<capsule>(getattr(pytype, local_key));
// Only consider this foreign loader if actually foreign and is a loader of the correct cpp type
if (foreign_typeinfo->module_local_load == &local_load
|| (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype)))
return false;
void* foreign_loader_void_ptr =
foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo);
if (foreign_loader_void_ptr != nullptr) {
auto foreign_loader = std::unique_ptr<modified_type_caster_generic_load_impl>(
static_cast<modified_type_caster_generic_load_impl *>(foreign_loader_void_ptr));
// Magic number intentionally hard-coded for simplicity and maximum robustness.
if (foreign_loader->local_load_safety_guard != 1887406645) {
pybind11_fail(
"smart_holder_type_casters: Unexpected local_load_safety_guard,"
" possibly due to py::class_ holder mixup.");
}
if (loaded_v_h_cpptype != nullptr) {
pybind11_fail("smart_holder_type_casters: try_load_foreign_module_local failure.");
}
loaded_v_h = foreign_loader->loaded_v_h;
loaded_v_h_cpptype = foreign_loader->loaded_v_h_cpptype;
implicit_cast = foreign_loader->implicit_cast;
return true;
}
return false;
}
// Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant
// bits of code between here and copyable_holder_caster where the two classes need different
// logic (without having to resort to virtual inheritance).
template <typename ThisT>
PYBIND11_NOINLINE bool load_impl(handle src, bool convert) {
if (!src) return false;
if (!typeinfo) return try_load_foreign_module_local(src);
if (src.is_none()) {
// Defer accepting None to other overloads (if we aren't in convert mode):
if (!convert) return false;
loaded_v_h = value_and_holder();
return true;
}
auto &this_ = static_cast<ThisT &>(*this);
PyTypeObject *srctype = Py_TYPE(src.ptr());
// Case 1: If src is an exact type match for the target type then we can reinterpret_cast
// the instance's value pointer to the target type:
if (srctype == typeinfo->type) {
this_.load_value_and_holder(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
return true;
}
// Case 2: We have a derived class
else if (PyType_IsSubtype(srctype, typeinfo->type)) {
auto &bases = all_type_info(srctype); // subtype bases
bool no_cpp_mi = typeinfo->simple_type;
// Case 2a: the python type is a Python-inherited derived class that inherits from just
// one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of
// the right type and we can use reinterpret_cast.
// (This is essentially the same as case 2b, but because not using multiple inheritance
// is extremely common, we handle it specially to avoid the loop iterator and type
// pointer lookup overhead)
if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) {
this_.load_value_and_holder(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
loaded_v_h_cpptype = bases.front()->cpptype;
reinterpret_cast_deemed_ok = true;
return true;
}
// Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if
// we can find an exact match (or, for a simple C++ type, an inherited match); if so, we
// can safely reinterpret_cast to the relevant pointer.
else if (bases.size() > 1) {
for (auto base : bases) {
if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) {
this_.load_value_and_holder(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base));
loaded_v_h_cpptype = base->cpptype;
reinterpret_cast_deemed_ok = true;
return true;
}
}
}
// Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match
// in the registered bases, above, so try implicit casting (needed for proper C++ casting
// when MI is involved).
if (this_.try_implicit_casts(src, convert)) {
return true;
}
}
// Perform an implicit conversion
if (convert) {
for (auto &converter : typeinfo->implicit_conversions) {
auto temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
if (load_impl<ThisT>(temp, false)) {
loader_life_support::add_patient(temp);
return true;
}
}
if (this_.try_direct_conversions(src))
return true;
}
// Failed to match local typeinfo. Try again with global.
if (typeinfo->module_local) {
if (auto gtype = get_global_type_info(*typeinfo->cpptype)) {
typeinfo = gtype;
return load(src, false);
}
}
// Global typeinfo has precedence over foreign module_local
return try_load_foreign_module_local(src);
}
const type_info *typeinfo = nullptr;
const std::type_info *cpptype = nullptr;
void *unowned_void_ptr_from_direct_conversion = nullptr;
const std::type_info *loaded_v_h_cpptype = nullptr;
void *(*implicit_cast)(void *) = nullptr;
value_and_holder loaded_v_h;
bool reinterpret_cast_deemed_ok = false;
// Magic number intentionally hard-coded, to guard against class_ holder mixups.
// Ideally type_caster_generic would have a similar guard, but this requires a change there.
std::size_t local_load_safety_guard = 1887406645; // 32-bit compatible value for portability.
};
// clang-format on
struct smart_holder_type_caster_class_hooks : is_smart_holder_type_caster_base_tag {
static decltype(&modified_type_caster_generic_load_impl::local_load)
get_local_load_function_ptr() {
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) {
// 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)));
if (!v_h.instance_registered()) {
register_instance(inst, v_h.value_ptr(), v_h.type);
v_h.set_instance_registered();
}
using holder_type = pybindit::memory::smart_holder;
if (holder_void_ptr) {
// Note: inst->owned ignored.
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>()));
} else {
new (std::addressof(v_h.holder<holder_type>()))
holder_type(holder_type::from_raw_ptr_unowned(v_h.value_ptr<T>()));
}
v_h.set_holder_constructed();
}
};
template <typename T>
struct smart_holder_type_caster_load {
using holder_type = pybindit::memory::smart_holder;
bool load(handle src, bool convert) {
static_assert(is_smart_holder_type_caster<T>::value, "Internal consistency error.");
load_impl = modified_type_caster_generic_load_impl(typeid(T));
if (!load_impl.load(src, convert))
return false;
return true;
}
T *loaded_as_raw_ptr_unowned() const {
void *void_ptr = load_impl.unowned_void_ptr_from_direct_conversion;
if (void_ptr == nullptr) {
if (have_holder()) {
throw_if_uninitialized_or_disowned_holder();
void_ptr = holder().template as_raw_ptr_unowned<void>();
} else if (load_impl.loaded_v_h.vh != nullptr)
void_ptr = load_impl.loaded_v_h.value_ptr();
if (void_ptr == nullptr)
return nullptr;
}
return convert_type(void_ptr);
}
T &loaded_as_lvalue_ref() const {
T *raw_ptr = loaded_as_raw_ptr_unowned();
if (raw_ptr == nullptr)
throw reference_cast_error();
return *raw_ptr;
}
std::shared_ptr<T> loaded_as_shared_ptr() const {
if (load_impl.unowned_void_ptr_from_direct_conversion != nullptr)
throw cast_error("Unowned pointer from direct conversion cannot be converted to a"
" std::shared_ptr.");
if (!have_holder())
return nullptr;
throw_if_uninitialized_or_disowned_holder();
std::shared_ptr<void> void_ptr = holder().template as_shared_ptr<void>();
return std::shared_ptr<T>(void_ptr, convert_type(void_ptr.get()));
}
template <typename D>
std::unique_ptr<T, D> loaded_as_unique_ptr(const char *context = "loaded_as_unique_ptr") {
if (load_impl.unowned_void_ptr_from_direct_conversion != nullptr)
throw cast_error("Unowned pointer from direct conversion cannot be converted to a"
" std::unique_ptr.");
if (!have_holder())
return nullptr;
throw_if_uninitialized_or_disowned_holder();
holder().template ensure_compatible_rtti_uqp_del<T, D>(context);
holder().ensure_use_count_1(context);
auto raw_void_ptr = holder().template as_raw_ptr_unowned<void>();
// SMART_HOLDER_WIP: MISSING: Safety checks for type conversions
// (T must be polymorphic or meet certain other conditions).
T *raw_type_ptr = convert_type(raw_void_ptr);
// Critical transfer-of-ownership section. This must stay together.
holder().release_ownership();
auto result = std::unique_ptr<T, D>(raw_type_ptr);
void *value_void_ptr = load_impl.loaded_v_h.value_ptr();
if (value_void_ptr != raw_void_ptr) {
pybind11_fail("smart_holder_type_casters: loaded_as_unique_ptr failure:"
" value_void_ptr != raw_void_ptr");
}
load_impl.loaded_v_h.value_ptr() = nullptr;
deregister_instance(load_impl.loaded_v_h.inst, value_void_ptr, load_impl.loaded_v_h.type);
return result;
}
private:
modified_type_caster_generic_load_impl load_impl;
bool have_holder() const {
return load_impl.loaded_v_h.vh != nullptr && load_impl.loaded_v_h.holder_constructed();
}
holder_type &holder() const { return load_impl.loaded_v_h.holder<holder_type>(); }
// have_holder() must be true or this function will fail.
void throw_if_uninitialized_or_disowned_holder() const {
if (!holder().is_populated) {
pybind11_fail("Missing value for wrapped C++ type:"
" Python instance is uninitialized.");
}
if (!holder().has_pointee()) {
throw cast_error("Missing value for wrapped C++ type:"
" Python instance was disowned.");
}
}
T *convert_type(void *void_ptr) const {
if (void_ptr != nullptr && load_impl.loaded_v_h_cpptype != nullptr
&& !load_impl.reinterpret_cast_deemed_ok && load_impl.implicit_cast != nullptr) {
void_ptr = load_impl.implicit_cast(void_ptr);
}
return static_cast<T *>(void_ptr);
}
};
// SMART_HOLDER_WIP: Needs refactoring of existing pybind11 code.
struct make_constructor : private type_caster_base<int> { // Any type, nothing special about int.
using type_caster_base<int>::Constructor;
using type_caster_base<int>::make_copy_constructor;
using type_caster_base<int>::make_move_constructor;
};
template <typename T>
struct smart_holder_type_caster : smart_holder_type_caster_load<T>,
smart_holder_type_caster_class_hooks {
static constexpr auto name = _<T>();
// static handle cast(T, ...)
// is redundant (leads to ambiguous overloads).
static handle cast(T &&src, return_value_policy /*policy*/, handle parent) {
// type_caster_base BEGIN
// clang-format off
return cast(&src, return_value_policy::move, parent);
// clang-format on
// type_caster_base END
}
static handle cast(T const &src, return_value_policy policy, handle parent) {
// type_caster_base BEGIN
// clang-format off
if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference)
policy = return_value_policy::copy;
return cast(&src, policy, parent);
// clang-format on
// type_caster_base END
}
static handle cast(T &src, return_value_policy policy, handle parent) {
return cast(const_cast<T const &>(src), policy, parent); // Mutbl2Const
}
static handle cast(T const *src, return_value_policy policy, handle parent) {
auto st = type_caster_base<T>::src_and_type(src);
return cast_const_raw_ptr( // Originally type_caster_generic::cast.
st.first,
policy,
parent,
st.second,
make_constructor::make_copy_constructor(src),
make_constructor::make_move_constructor(src));
}
static handle cast(T *src, return_value_policy policy, handle parent) {
return cast(const_cast<T const *>(src), policy, parent); // Mutbl2Const
}
#if defined(_MSC_VER) && _MSC_VER < 1910
// Working around MSVC 2015 bug. const-correctness is lost.
// SMART_HOLDER_WIP: IMPROVABLE: make common code work with MSVC 2015.
template <typename T_>
using cast_op_type = detail::cast_op_type<T_>;
#else
template <typename T_>
using cast_op_type = conditional_t<
std::is_same<remove_reference_t<T_>, T const *>::value,
T const *,
conditional_t<std::is_same<remove_reference_t<T_>, T *>::value,
T *,
conditional_t<std::is_same<T_, T const &>::value, T const &, T &>>>;
#endif
// The const operators here prove that the existing type_caster mechanism already supports
// const-correctness. However, fully implementing const-correctness inside this type_caster
// is still a major project.
operator T const &() const {
return const_cast<smart_holder_type_caster *>(this)->loaded_as_lvalue_ref();
}
operator T const *() const {
return const_cast<smart_holder_type_caster *>(this)->loaded_as_raw_ptr_unowned();
}
operator T &() { return this->loaded_as_lvalue_ref(); }
operator T *() { return this->loaded_as_raw_ptr_unowned(); }
// Originally type_caster_generic::cast.
PYBIND11_NOINLINE static handle cast_const_raw_ptr(const void *_src,
return_value_policy policy,
handle parent,
const detail::type_info *tinfo,
void *(*copy_constructor)(const void *),
void *(*move_constructor)(const void *),
const void *existing_holder = nullptr) {
if (!tinfo) // no type info: error will be set already
return handle();
void *src = const_cast<void *>(_src);
if (src == nullptr)
return none().release();
if (handle existing_inst = find_registered_python_instance(src, tinfo))
return existing_inst;
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
auto wrapper = reinterpret_cast<instance *>(inst.ptr());
wrapper->owned = false;
void *&valueptr = values_and_holders(wrapper).begin()->value_ptr();
switch (policy) {
case return_value_policy::automatic:
case return_value_policy::take_ownership:
valueptr = src;
wrapper->owned = true;
break;
case return_value_policy::automatic_reference:
case return_value_policy::reference:
valueptr = src;
wrapper->owned = false;
break;
case return_value_policy::copy:
if (copy_constructor)
valueptr = copy_constructor(src);
else {
#if defined(NDEBUG)
throw cast_error("return_value_policy = copy, but type is "
"non-copyable! (compile in debug mode for details)");
#else
std::string type_name(tinfo->cpptype->name());
detail::clean_type_id(type_name);
throw cast_error("return_value_policy = copy, but type " + type_name
+ " is non-copyable!");
#endif
}
wrapper->owned = true;
break;
case return_value_policy::move:
if (move_constructor)
valueptr = move_constructor(src);
else if (copy_constructor)
valueptr = copy_constructor(src);
else {
#if defined(NDEBUG)
throw cast_error("return_value_policy = move, but type is neither "
"movable nor copyable! "
"(compile in debug mode for details)");
#else
std::string type_name(tinfo->cpptype->name());
detail::clean_type_id(type_name);
throw cast_error("return_value_policy = move, but type " + type_name
+ " is neither movable nor copyable!");
#endif
}
wrapper->owned = true;
break;
case return_value_policy::reference_internal:
valueptr = src;
wrapper->owned = false;
keep_alive_impl(inst, parent);
break;
default:
throw cast_error("unhandled return_value_policy: should not happen!");
}
tinfo->init_instance(wrapper, existing_holder);
return inst.release();
}
};
template <typename T>
struct smart_holder_type_caster<std::shared_ptr<T>> : smart_holder_type_caster_load<T>,
smart_holder_type_caster_class_hooks {
static constexpr auto name = _<std::shared_ptr<T>>();
static handle cast(const std::shared_ptr<T> &src, return_value_policy policy, handle parent) {
if (policy != return_value_policy::automatic
&& policy != return_value_policy::reference_internal) {
// SMART_HOLDER_WIP: IMPROVABLE: Error message.
throw cast_error("Invalid return_value_policy for shared_ptr.");
}
auto src_raw_ptr = src.get();
auto st = type_caster_base<T>::src_and_type(src_raw_ptr);
if (st.first == nullptr)
return none().release(); // PyErr was set already.
void *src_raw_void_ptr = static_cast<void *>(src_raw_ptr);
const detail::type_info *tinfo = st.second;
if (handle existing_inst = find_registered_python_instance(src_raw_void_ptr, tinfo))
// SMART_HOLDER_WIP: MISSING: Enforcement of consistency with existing smart_holder.
// SMART_HOLDER_WIP: MISSING: keep_alive.
return existing_inst;
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
auto *inst_raw_ptr = reinterpret_cast<instance *>(inst.ptr());
inst_raw_ptr->owned = true;
void *&valueptr = values_and_holders(inst_raw_ptr).begin()->value_ptr();
valueptr = src_raw_void_ptr;
auto smhldr = pybindit::memory::smart_holder::from_shared_ptr(src);
tinfo->init_instance(inst_raw_ptr, static_cast<const void *>(&smhldr));
if (policy == return_value_policy::reference_internal)
keep_alive_impl(inst, parent);
return inst.release();
}
template <typename>
using cast_op_type = std::shared_ptr<T>;
operator std::shared_ptr<T>() { return this->loaded_as_shared_ptr(); }
};
template <typename T>
struct smart_holder_type_caster<std::shared_ptr<T const>> : smart_holder_type_caster_load<T>,
smart_holder_type_caster_class_hooks {
static constexpr auto name = _<std::shared_ptr<T const>>();
static handle
cast(const std::shared_ptr<T const> &src, return_value_policy policy, handle parent) {
return smart_holder_type_caster<std::shared_ptr<T>>::cast(
std::const_pointer_cast<T>(src), // Const2Mutbl
policy,
parent);
}
template <typename>
using cast_op_type = std::shared_ptr<T const>;
operator std::shared_ptr<T const>() { return this->loaded_as_shared_ptr(); } // Mutbl2Const
};
template <typename T, typename D>
struct smart_holder_type_caster<std::unique_ptr<T, D>> : smart_holder_type_caster_load<T>,
smart_holder_type_caster_class_hooks {
static constexpr auto name = _<std::unique_ptr<T, D>>();
static handle cast(std::unique_ptr<T, D> &&src, return_value_policy policy, handle parent) {
if (policy != return_value_policy::automatic
&& policy != return_value_policy::reference_internal
&& policy != return_value_policy::move) {
// SMART_HOLDER_WIP: IMPROVABLE: Error message.
throw cast_error("Invalid return_value_policy for unique_ptr.");
}
auto src_raw_ptr = src.get();
auto st = type_caster_base<T>::src_and_type(src_raw_ptr);
if (st.first == nullptr)
return none().release(); // PyErr was set already.
void *src_raw_void_ptr = static_cast<void *>(src_raw_ptr);
const detail::type_info *tinfo = st.second;
if (find_registered_python_instance(src_raw_void_ptr, tinfo))
throw cast_error("Invalid unique_ptr: another instance owns this pointer already.");
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
auto *inst_raw_ptr = reinterpret_cast<instance *>(inst.ptr());
inst_raw_ptr->owned = true;
void *&valueptr = values_and_holders(inst_raw_ptr).begin()->value_ptr();
valueptr = src_raw_void_ptr;
auto smhldr = pybindit::memory::smart_holder::from_unique_ptr(std::move(src));
tinfo->init_instance(inst_raw_ptr, static_cast<const void *>(&smhldr));
if (policy == return_value_policy::reference_internal)
keep_alive_impl(inst, parent);
return inst.release();
}
template <typename>
using cast_op_type = std::unique_ptr<T, D>;
operator std::unique_ptr<T, D>() { return this->template loaded_as_unique_ptr<D>(); }
};
template <typename T, typename D>
struct smart_holder_type_caster<std::unique_ptr<T const, D>>
: smart_holder_type_caster_load<T>, smart_holder_type_caster_class_hooks {
static constexpr auto name = _<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
policy,
parent);
}
template <typename>
using cast_op_type = std::unique_ptr<T const, D>;
operator std::unique_ptr<T const, D>() { return this->template loaded_as_unique_ptr<D>(); }
};
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
# define PYBIND11_SMART_HOLDER_TYPE_CASTERS(T) \
namespace pybind11 { \
namespace detail { \
template <> \
class type_caster<T> : public smart_holder_type_caster<T> {}; \
template <> \
class type_caster<std::shared_ptr<T>> \
: public smart_holder_type_caster<std::shared_ptr<T>> {}; \
template <> \
class type_caster<std::shared_ptr<T const>> \
: public smart_holder_type_caster<std::shared_ptr<T const>> {}; \
template <typename D> \
class type_caster<std::unique_ptr<T, D>> \
: public smart_holder_type_caster<std::unique_ptr<T, D>> {}; \
template <typename D> \
class type_caster<std::unique_ptr<T const, D>> \
: public smart_holder_type_caster<std::unique_ptr<T const, D>> {}; \
} \
}
#endif
PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
// clang-format off
/*
pybind11/pybind11.h: Main header file of the C++11 python
binding generator library
......@@ -1240,22 +1241,26 @@ auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(
return pmf;
}
// clang-format on
template <typename T>
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
using default_holder_type = std::unique_ptr<T>;
#else
using default_holder_type = smart_holder;
#endif
// clang-format off
template <typename type_, typename... options>
class class_ : public detail::generic_type {
template <typename T> using is_subtype = detail::is_strict_base_of<type_, T>;
template <typename T> using is_base = detail::is_strict_base_of<T, type_>;
template <typename T>
// clang-format on
using is_holder = detail::any_of<detail::is_holder_type<type_, T>,
detail::all_of<detail::negation<is_base<T>>,
detail::negation<is_subtype<T>>,
detail::is_smart_holder_type_caster<type_>>>;
// clang-format off
// struct instead of using here to help MSVC:
template <typename T> struct is_valid_class_option :
detail::any_of<is_holder<T>, is_subtype<T>, is_base<T>> {};
......@@ -1286,18 +1291,24 @@ public:
none_of<std::is_same<multiple_inheritance, Extra>...>::value), // no multiple_inheritance attr
"Error: multiple inheritance bases must be specified via class_ template options");
static constexpr bool holder_is_smart_holder = std::is_same<holder_type, smart_holder>::value;
static constexpr bool type_caster_type_is_smart_holder_type_caster = detail::is_smart_holder_type_caster<type>::value;
static constexpr bool type_caster_type_is_type_caster_base_subtype = std::is_base_of<detail::type_caster_base<type>, detail::type_caster<type>>::value;
// clang-format on
static constexpr bool holder_is_smart_holder
= std::is_same<holder_type, smart_holder>::value;
static constexpr bool type_caster_type_is_smart_holder_type_caster
= detail::is_smart_holder_type_caster<type>::value;
static constexpr bool type_caster_type_is_type_caster_base_subtype
= std::is_base_of<detail::type_caster_base<type>, detail::type_caster<type>>::value;
// Necessary conditions, but not strict.
static_assert(!(detail::is_instantiation<std::unique_ptr, holder_type>::value &&
type_caster_type_is_smart_holder_type_caster),
"py::class_ holder vs type_caster mismatch:"
" missing PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS(T, std::unique_ptr<T>)?");
static_assert(!(detail::is_instantiation<std::shared_ptr, holder_type>::value &&
type_caster_type_is_smart_holder_type_caster),
"py::class_ holder vs type_caster mismatch:"
" missing PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS(T, std::shared_ptr<T>)?");
static_assert(
!(detail::is_instantiation<std::unique_ptr, holder_type>::value
&& type_caster_type_is_smart_holder_type_caster),
"py::class_ holder vs type_caster mismatch:"
" missing PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS(T, std::unique_ptr<T>)?");
static_assert(
!(detail::is_instantiation<std::shared_ptr, holder_type>::value
&& type_caster_type_is_smart_holder_type_caster),
"py::class_ holder vs type_caster mismatch:"
" missing PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS(T, std::shared_ptr<T>)?");
static_assert(!(holder_is_smart_holder && type_caster_type_is_type_caster_base_subtype),
"py::class_ holder vs type_caster mismatch:"
" missing PYBIND11_SMART_HOLDER_TYPE_CASTERS(T)?");
......@@ -1312,6 +1323,7 @@ public:
" missing PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS(T, ...)"
" or collision with custom py::detail::type_caster<T>?");
#endif
// clang-format off
type_record record;
record.scope = scope;
record.name = name;
......@@ -1322,7 +1334,7 @@ public:
record.init_instance = init_instance;
record.dealloc = dealloc;
// A better name would be uses_unique_ptr_holder.
// A more fitting name would be uses_unique_ptr_holder.
record.default_holder = detail::is_instantiation<std::unique_ptr, holder_type>::value;
set_operator_new<type>(&record);
......@@ -1541,19 +1553,19 @@ public:
}
private:
template <
typename T = type,
detail::enable_if_t<!detail::is_smart_holder_type_caster<T>::value, int> = 0>
// clang-format on
template <typename T = type,
detail::enable_if_t<!detail::is_smart_holder_type_caster<T>::value, int> = 0>
void generic_type_initialize(const detail::type_record &record) {
generic_type::initialize(record, &detail::type_caster_generic::local_load);
}
template <
typename T = type,
detail::enable_if_t<detail::is_smart_holder_type_caster<T>::value, int> = 0>
template <typename T = type,
detail::enable_if_t<detail::is_smart_holder_type_caster<T>::value, int> = 0>
void generic_type_initialize(const detail::type_record &record) {
generic_type::initialize(record, detail::type_caster<T>::get_local_load_function_ptr());
}
// clang-format off
/// Initialize holder object, variant 1: object derives from enable_shared_from_this
template <typename T>
......@@ -1611,12 +1623,13 @@ private:
init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr<type>());
}
template <
typename T = type,
detail::enable_if_t<detail::is_smart_holder_type_caster<T>::value, int> = 0>
// clang-format on
template <typename T = type,
detail::enable_if_t<detail::is_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);
}
// clang-format off
/// Deallocates an instance; via holder, if constructed; otherwise via operator delete.
static void dealloc(detail::value_and_holder &v_h) {
......
......@@ -42,7 +42,9 @@ detail_headers = {
"include/pybind11/detail/descr.h",
"include/pybind11/detail/init.h",
"include/pybind11/detail/internals.h",
"include/pybind11/detail/smart_holder_init_inline_include.h",
"include/pybind11/detail/smart_holder_poc.h",
"include/pybind11/detail/smart_holder_type_casters_inline_include.h",
"include/pybind11/detail/typeid.h",
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment