Replacing detail::is_smart_holder<H> in cast.h with detail::is_smart_holder_type_caster<T>.

Moving get_local_load_function_ptr, init_instance_for_type to smart_holder_type_caster_class_hooks.
Expanding static_assert in py::type::handle_of<> to accommodate smart_holder_type_casters.
This commit is contained in:
Ralf W. Grosse-Kunstleve 2021-01-28 14:25:19 -08:00
parent 87acc89b21
commit 742bc0eaf7
5 changed files with 93 additions and 61 deletions

View File

@ -1628,7 +1628,6 @@ using type_caster_holder = conditional_t<is_copy_constructible<holder_type>::val
copyable_holder_caster<type, holder_type>,
move_only_holder_caster<type, holder_type>>;
template <typename T, bool Value = false> struct is_smart_holder { static constexpr bool value = Value; };
template <typename T, bool Value = false> struct always_construct_holder { static constexpr bool value = Value; };
/// Create a specialization for custom holder types (silently ignores std::shared_ptr)
@ -1643,8 +1642,7 @@ template <typename T, bool Value = false> struct always_construct_holder { stati
// PYBIND11_DECLARE_HOLDER_TYPE holder types:
template <typename base, typename holder> struct is_holder_type :
detail::any_of<std::is_base_of<detail::type_caster_holder<base, holder>, detail::type_caster<holder>>,
detail::is_smart_holder<holder>> {};
std::is_base_of<detail::type_caster_holder<base, holder>, detail::type_caster<holder>> {};
// Specialization for always-supported unique_ptr holders:
template <typename base, typename deleter> struct is_holder_type<base, std::unique_ptr<base, deleter>> :
std::true_type {};
@ -2263,15 +2261,22 @@ object object_api<Derived>::call(Args &&...args) const {
return operator()<policy>(std::forward<Args>(args)...);
}
template <typename T, typename SFINAE = void>
struct is_smart_holder_type_caster : std::false_type {};
template <typename T>
struct is_smart_holder_type_caster<
T,
enable_if_t<type_caster<T>::is_smart_holder_type_caster::value, void>> : std::true_type {};
PYBIND11_NAMESPACE_END(detail)
template<typename T>
handle type::handle_of() {
static_assert(
std::is_base_of<detail::type_caster_generic, detail::make_caster<T>>::value,
"py::type::of<T> only supports the case where T is a registered C++ types."
);
static_assert(
detail::any_of<std::is_base_of<detail::type_caster_generic, detail::make_caster<T>>,
detail::is_smart_holder_type_caster<T>>::value,
"py::type::of<T> only supports the case where T is a registered C++ types.");
return detail::get_type_handle(typeid(T), true);
}

View File

@ -228,6 +228,41 @@ public:
};
// clang-format on
struct smart_holder_type_caster_class_hooks {
using is_smart_holder_type_caster = std::true_type;
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;
@ -305,7 +340,8 @@ struct make_constructor : private type_caster_base<int> { // Any type, nothing s
};
template <typename T>
struct smart_holder_type_caster : smart_holder_type_caster_load<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, ...)
@ -462,7 +498,8 @@ struct smart_holder_type_caster : smart_holder_type_caster_load<T> {
};
template <typename T>
struct smart_holder_type_caster<std::shared_ptr<T>> : smart_holder_type_caster_load<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) {
@ -506,7 +543,8 @@ struct smart_holder_type_caster<std::shared_ptr<T>> : smart_holder_type_caster_l
};
template <typename T>
struct smart_holder_type_caster<std::shared_ptr<T const>> : smart_holder_type_caster_load<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
@ -524,7 +562,8 @@ struct smart_holder_type_caster<std::shared_ptr<T const>> : smart_holder_type_ca
};
template <typename T>
struct smart_holder_type_caster<std::unique_ptr<T>> : smart_holder_type_caster_load<T> {
struct smart_holder_type_caster<std::unique_ptr<T>> : smart_holder_type_caster_load<T>,
smart_holder_type_caster_class_hooks {
static constexpr auto name = _<std::unique_ptr<T>>();
static handle cast(std::unique_ptr<T> &&src, return_value_policy policy, handle parent) {
@ -566,7 +605,8 @@ struct smart_holder_type_caster<std::unique_ptr<T>> : smart_holder_type_caster_l
};
template <typename T>
struct smart_holder_type_caster<std::unique_ptr<T const>> : smart_holder_type_caster_load<T> {
struct smart_holder_type_caster<std::unique_ptr<T const>> : smart_holder_type_caster_load<T>,
smart_holder_type_caster_class_hooks {
static constexpr auto name = _<std::unique_ptr<T const>>();
static handle cast(std::unique_ptr<T const> &&src, return_value_policy policy, handle parent) {
@ -582,42 +622,6 @@ struct smart_holder_type_caster<std::unique_ptr<T const>> : smart_holder_type_ca
operator std::unique_ptr<T const>() { return this->loaded_as_unique_ptr(); }
};
template <>
struct is_smart_holder<pybindit::memory::smart_holder>
: is_smart_holder<pybindit::memory::smart_holder, true> {
static decltype(&modified_type_caster_generic_load_impl::local_load)
get_type_caster_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();
}
};
#define PYBIND11_SMART_HOLDER_TYPE_CASTERS(T) \
namespace pybind11 { \
namespace detail { \

View File

@ -1242,9 +1242,13 @@ auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(
template <typename type_, typename... options>
class class_ : public detail::generic_type {
template <typename T> using is_holder = detail::is_holder_type<type_, T>;
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>
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_>>>;
// 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>> {};
@ -1502,14 +1506,19 @@ public:
}
private:
template <typename H = holder_type, detail::enable_if_t<!detail::is_smart_holder<H>::value, int> = 0>
template <typename T = type,
detail::enable_if_t<
std::is_base_of<detail::type_caster_generic, detail::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 H = holder_type, detail::enable_if_t<detail::is_smart_holder<H>::value, int> = 0>
template <
typename T = type,
detail::enable_if_t<detail::type_caster<T>::is_smart_holder_type_caster::value, int> = 0>
void generic_type_initialize(const detail::type_record &record) {
generic_type::initialize(record, detail::is_smart_holder<H>::get_type_caster_local_load_function_ptr());
generic_type::initialize(record, detail::type_caster<T>::get_local_load_function_ptr());
}
/// Initialize holder object, variant 1: object derives from enable_shared_from_this
@ -1556,7 +1565,10 @@ private:
/// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an
/// optional pointer to an existing holder to use; if not specified and the instance is
/// `.owned`, a new holder will be constructed to manage the value pointer.
template <typename H = holder_type, detail::enable_if_t<!detail::is_smart_holder<H>::value, int> = 0>
template <typename T = type,
detail::enable_if_t<
std::is_base_of<detail::type_caster_generic, detail::type_caster<T>>::value,
int> = 0>
static void init_instance(detail::instance *inst, const void *holder_ptr) {
auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type)));
if (!v_h.instance_registered()) {
@ -1566,9 +1578,11 @@ private:
init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr<type>());
}
template <typename H = holder_type, detail::enable_if_t<detail::is_smart_holder<H>::value, int> = 0>
template <
typename T = type,
detail::enable_if_t<detail::type_caster<T>::is_smart_holder_type_caster::value, int> = 0>
static void init_instance(detail::instance *inst, const void *holder_ptr) {
detail::is_smart_holder<H>::template init_instance_for_type<type>(inst, holder_ptr);
detail::type_caster<T>::template init_instance_for_type<type>(inst, holder_ptr);
}
/// Deallocates an instance; via holder, if constructed; otherwise via operator delete.

View File

@ -93,6 +93,10 @@ TEST_SUBMODULE(class_sh_basic, m) {
// These require selected functions above to work first, as indicated:
m.def("get_mtxt", get_mtxt); // pass_cref_atyp
m.def("unique_ptr_roundtrip", unique_ptr_roundtrip); // pass_uqmp_atyp, rtrn_uqmp_atyp
m.def("py_type_handle_of_atyp", []() {
return py::type::handle_of<atyp>(); // Exercises static_cast in this function.
});
}
} // namespace class_sh_basic

View File

@ -5,12 +5,12 @@ from pybind11_tests import class_sh_basic as m
def test_atyp_constructors():
e = m.atyp()
assert e.__class__.__name__ == "atyp"
e = m.atyp("")
assert e.__class__.__name__ == "atyp"
e = m.atyp("txtm")
assert e.__class__.__name__ == "atyp"
obj = m.atyp()
assert obj.__class__.__name__ == "atyp"
obj = m.atyp("")
assert obj.__class__.__name__ == "atyp"
obj = m.atyp("txtm")
assert obj.__class__.__name__ == "atyp"
def test_cast():
@ -80,3 +80,8 @@ def test_unique_ptr_roundtrip(num_round_trips=1000):
# Ensure the returned object is a different Python instance.
assert id_rtrn != id_orig
id_orig = id_rtrn
def test_py_type_handle_of_atyp():
obj = m.py_type_handle_of_atyp()
assert obj.__class__.__name__ == "pybind11_type"