diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 65a367642..44764e3df 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1628,7 +1628,6 @@ using type_caster_holder = conditional_t::val copyable_holder_caster, move_only_holder_caster>; -template struct is_smart_holder { static constexpr bool value = Value; }; template 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 struct always_construct_holder { stati // PYBIND11_DECLARE_HOLDER_TYPE holder types: template struct is_holder_type : - detail::any_of, detail::type_caster>, - detail::is_smart_holder> {}; + std::is_base_of, detail::type_caster> {}; // Specialization for always-supported unique_ptr holders: template struct is_holder_type> : std::true_type {}; @@ -2263,15 +2261,22 @@ object object_api::call(Args &&...args) const { return operator()(std::forward(args)...); } +template +struct is_smart_holder_type_caster : std::false_type {}; +template +struct is_smart_holder_type_caster< + T, + enable_if_t::is_smart_holder_type_caster::value, void>> : std::true_type {}; + PYBIND11_NAMESPACE_END(detail) template handle type::handle_of() { - static_assert( - std::is_base_of>::value, - "py::type::of only supports the case where T is a registered C++ types." - ); + static_assert( + detail::any_of>, + detail::is_smart_holder_type_caster>::value, + "py::type::of only supports the case where T is a registered C++ types."); return detail::get_type_handle(typeid(T), true); } diff --git a/include/pybind11/detail/smart_holder_type_casters.h b/include/pybind11/detail/smart_holder_type_casters.h index 28874d973..ec0aa774a 100644 --- a/include/pybind11/detail/smart_holder_type_casters.h +++ b/include/pybind11/detail/smart_holder_type_casters.h @@ -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 + 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(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_void_ptr); + new (std::addressof(v_h.holder())) holder_type(std::move(*holder_ptr)); + } else if (inst->owned) { + new (std::addressof(v_h.holder())) + holder_type(holder_type::from_raw_ptr_take_ownership(v_h.value_ptr())); + } else { + new (std::addressof(v_h.holder())) + holder_type(holder_type::from_raw_ptr_unowned(v_h.value_ptr())); + } + v_h.set_holder_constructed(); + } +}; + template struct smart_holder_type_caster_load { using holder_type = pybindit::memory::smart_holder; @@ -305,7 +340,8 @@ struct make_constructor : private type_caster_base { // Any type, nothing s }; template -struct smart_holder_type_caster : smart_holder_type_caster_load { +struct smart_holder_type_caster : smart_holder_type_caster_load, + smart_holder_type_caster_class_hooks { static constexpr auto name = _(); // static handle cast(T, ...) @@ -462,7 +498,8 @@ struct smart_holder_type_caster : smart_holder_type_caster_load { }; template -struct smart_holder_type_caster> : smart_holder_type_caster_load { +struct smart_holder_type_caster> : smart_holder_type_caster_load, + smart_holder_type_caster_class_hooks { static constexpr auto name = _>(); static handle cast(const std::shared_ptr &src, return_value_policy policy, handle parent) { @@ -506,7 +543,8 @@ struct smart_holder_type_caster> : smart_holder_type_caster_l }; template -struct smart_holder_type_caster> : smart_holder_type_caster_load { +struct smart_holder_type_caster> : smart_holder_type_caster_load, + smart_holder_type_caster_class_hooks { static constexpr auto name = _>(); static handle @@ -524,7 +562,8 @@ struct smart_holder_type_caster> : smart_holder_type_ca }; template -struct smart_holder_type_caster> : smart_holder_type_caster_load { +struct smart_holder_type_caster> : smart_holder_type_caster_load, + smart_holder_type_caster_class_hooks { static constexpr auto name = _>(); static handle cast(std::unique_ptr &&src, return_value_policy policy, handle parent) { @@ -566,7 +605,8 @@ struct smart_holder_type_caster> : smart_holder_type_caster_l }; template -struct smart_holder_type_caster> : smart_holder_type_caster_load { +struct smart_holder_type_caster> : smart_holder_type_caster_load, + smart_holder_type_caster_class_hooks { static constexpr auto name = _>(); static handle cast(std::unique_ptr &&src, return_value_policy policy, handle parent) { @@ -582,42 +622,6 @@ struct smart_holder_type_caster> : smart_holder_type_ca operator std::unique_ptr() { return this->loaded_as_unique_ptr(); } }; -template <> -struct is_smart_holder - : is_smart_holder { - - 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 - 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(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_void_ptr); - new (std::addressof(v_h.holder())) holder_type(std::move(*holder_ptr)); - } else if (inst->owned) { - new (std::addressof(v_h.holder())) - holder_type(holder_type::from_raw_ptr_take_ownership(v_h.value_ptr())); - } else { - new (std::addressof(v_h.holder())) - holder_type(holder_type::from_raw_ptr_unowned(v_h.value_ptr())); - } - v_h.set_holder_constructed(); - } -}; - #define PYBIND11_SMART_HOLDER_TYPE_CASTERS(T) \ namespace pybind11 { \ namespace detail { \ diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index df1c2d4f3..efb460b1f 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1242,9 +1242,13 @@ auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)( template class class_ : public detail::generic_type { - template using is_holder = detail::is_holder_type; template using is_subtype = detail::is_strict_base_of; template using is_base = detail::is_strict_base_of; + template + using is_holder = detail::any_of, + detail::all_of>, + detail::negation>, + detail::is_smart_holder_type_caster>>; // struct instead of using here to help MSVC: template struct is_valid_class_option : detail::any_of, is_subtype, is_base> {}; @@ -1502,14 +1506,19 @@ public: } private: - template ::value, int> = 0> + template >::value, + int> = 0> void generic_type_initialize(const detail::type_record &record) { generic_type::initialize(record, &detail::type_caster_generic::local_load); } - template ::value, int> = 0> + template < + typename T = type, + detail::enable_if_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::get_type_caster_local_load_function_ptr()); + generic_type::initialize(record, detail::type_caster::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 ::value, int> = 0> + template >::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()); } - template ::value, int> = 0> + template < + typename T = type, + detail::enable_if_t::is_smart_holder_type_caster::value, int> = 0> static void init_instance(detail::instance *inst, const void *holder_ptr) { - detail::is_smart_holder::template init_instance_for_type(inst, holder_ptr); + detail::type_caster::template init_instance_for_type(inst, holder_ptr); } /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. diff --git a/tests/test_class_sh_basic.cpp b/tests/test_class_sh_basic.cpp index 96c9261fe..b638fd854 100644 --- a/tests/test_class_sh_basic.cpp +++ b/tests/test_class_sh_basic.cpp @@ -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(); // Exercises static_cast in this function. + }); } } // namespace class_sh_basic diff --git a/tests/test_class_sh_basic.py b/tests/test_class_sh_basic.py index 21a93b5dc..62d365c98 100644 --- a/tests/test_class_sh_basic.py +++ b/tests/test_class_sh_basic.py @@ -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"