@@ -545,6 +545,74 @@ PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_i
545545 });
546546}
547547
548+ // Information about how type_caster_generic::cast() can obtain its source object
549+ struct cast_sources {
550+ // A type-erased pointer and the type it points to
551+ struct raw_source {
552+ const void *cppobj;
553+ const std::type_info *cpptype;
554+ };
555+
556+ // A C++ pointer and the Python type info we will convert it to;
557+ // we expect that cppobj points to something of type tinfo->cpptype
558+ struct resolved_source {
559+ const void *cppobj;
560+ const type_info *tinfo;
561+ };
562+
563+ // Use the given pointer with its compile-time type, possibly downcast
564+ // via polymorphic_type_hook()
565+ template <typename itype>
566+ cast_sources (const itype *ptr); // NOLINT(google-explicit-constructor)
567+
568+ // Use the given pointer and type
569+ // NOLINTNEXTLINE(google-explicit-constructor)
570+ cast_sources (const raw_source &orig) : original(orig) { result = resolve (); }
571+
572+ // Use the given object and pybind11 type info. NB: if tinfo is null,
573+ // this does not provide enough information to use a foreign type or
574+ // to render a useful error message
575+ cast_sources (const void *obj, const detail::type_info *tinfo)
576+ : original{obj, tinfo ? tinfo->cpptype : nullptr }, result{obj, tinfo} {}
577+
578+ // The object passed to cast(), with its static type.
579+ // original.type must not be null if resolve() will be called.
580+ // original.obj may be null if we're converting nullptr to a Python None
581+ raw_source original;
582+
583+ // A more-derived version of `original` provided by a
584+ // polymorphic_type_hook. downcast.type may be null if this is not
585+ // a relevant concept for the current cast.
586+ raw_source downcast{};
587+
588+ // The source to use for this cast, and the corresponding pybind11
589+ // type_info. If the type_info is null, then pybind11 doesn't know
590+ // about this type.
591+ resolved_source result;
592+
593+ // Returns true if the cast will use a pybind11 type that uses
594+ // a smart holder.
595+ bool creates_smart_holder () const {
596+ return result.tinfo != nullptr
597+ && result.tinfo ->holder_enum_v == detail::holder_enum_t ::smart_holder;
598+ }
599+
600+ private:
601+ resolved_source resolve () {
602+ if (downcast.cpptype ) {
603+ if (same_type (*original.cpptype , *downcast.cpptype )) {
604+ downcast.cpptype = nullptr ;
605+ } else if (const auto *tpi = get_type_info (*downcast.cpptype )) {
606+ return {downcast.cppobj , tpi};
607+ }
608+ }
609+ if (const auto *tpi = get_type_info (*original.cpptype )) {
610+ return {original.cppobj , tpi};
611+ }
612+ return {nullptr , nullptr };
613+ }
614+ };
615+
548616// Forward declarations
549617void keep_alive_impl (handle nurse, handle patient);
550618inline PyObject *make_new_instance (PyTypeObject *type);
@@ -607,18 +675,18 @@ template <typename T, typename D>
607675handle smart_holder_from_unique_ptr (std::unique_ptr<T, D> &&src,
608676 return_value_policy policy,
609677 handle parent,
610- const std::pair< const void *, const type_info *> &st ) {
678+ const cast_sources::resolved_source &cs ) {
611679 if (policy == return_value_policy::copy) {
612680 throw cast_error (" return_value_policy::copy is invalid for unique_ptr." );
613681 }
614682 if (!src) {
615683 return none ().release ();
616684 }
617- // st.first is the subobject pointer appropriate for tinfo (may differ from src.get()
685+ // cs.cppobj is the subobject pointer appropriate for tinfo (may differ from src.get()
618686 // under MI/VI). Use this for Python identity/registration, but keep ownership on T*.
619- void *src_raw_void_ptr = const_cast <void *>(st. first );
620- assert (st. second != nullptr );
621- const detail::type_info *tinfo = st. second ;
687+ void *src_raw_void_ptr = const_cast <void *>(cs. cppobj );
688+ assert (cs. tinfo != nullptr );
689+ const detail::type_info *tinfo = cs. tinfo ;
622690 if (handle existing_inst = find_registered_python_instance (src_raw_void_ptr, tinfo)) {
623691 auto *self_life_support = tinfo->get_trampoline_self_life_support (src.get ());
624692 if (self_life_support != nullptr ) {
@@ -665,20 +733,20 @@ template <typename T, typename D>
665733handle smart_holder_from_unique_ptr (std::unique_ptr<T const , D> &&src,
666734 return_value_policy policy,
667735 handle parent,
668- const std::pair< const void *, const type_info *> &st ) {
736+ const cast_sources::resolved_source &cs ) {
669737 return smart_holder_from_unique_ptr (
670738 std::unique_ptr<T, D>(const_cast <T *>(src.release ()),
671739 std::move (src.get_deleter ())), // Const2Mutbl
672740 policy,
673741 parent,
674- st );
742+ cs );
675743}
676744
677745template <typename T>
678746handle smart_holder_from_shared_ptr (const std::shared_ptr<T> &src,
679747 return_value_policy policy,
680748 handle parent,
681- const std::pair< const void *, const type_info *> &st ) {
749+ const cast_sources::resolved_source &cs ) {
682750 switch (policy) {
683751 case return_value_policy::automatic:
684752 case return_value_policy::automatic_reference:
@@ -697,11 +765,11 @@ handle smart_holder_from_shared_ptr(const std::shared_ptr<T> &src,
697765 return none ().release ();
698766 }
699767
700- // st.first is the subobject pointer appropriate for tinfo (may differ from src.get()
768+ // cs.cppobj is the subobject pointer appropriate for tinfo (may differ from src.get()
701769 // under MI/VI). Use this for Python identity/registration, but keep ownership on T*.
702- void *src_raw_void_ptr = const_cast <void *>(st. first );
703- assert (st. second != nullptr );
704- const detail::type_info *tinfo = st. second ;
770+ void *src_raw_void_ptr = const_cast <void *>(cs. cppobj );
771+ assert (cs. tinfo != nullptr );
772+ const detail::type_info *tinfo = cs. tinfo ;
705773 if (handle existing_inst = find_registered_python_instance (src_raw_void_ptr, tinfo)) {
706774 // PYBIND11:REMINDER: MISSING: Enforcement of consistency with existing smart_holder.
707775 // PYBIND11:REMINDER: MISSING: keep_alive.
@@ -728,11 +796,11 @@ template <typename T>
728796handle smart_holder_from_shared_ptr (const std::shared_ptr<T const > &src,
729797 return_value_policy policy,
730798 handle parent,
731- const std::pair< const void *, const type_info *> &st ) {
799+ const cast_sources::resolved_source &cs ) {
732800 return smart_holder_from_shared_ptr (std::const_pointer_cast<T>(src), // Const2Mutbl
733801 policy,
734802 parent,
735- st );
803+ cs );
736804}
737805
738806struct shared_ptr_parent_life_support {
@@ -925,21 +993,39 @@ class type_caster_generic {
925993
926994 bool load (handle src, bool convert) { return load_impl<type_caster_generic>(src, convert); }
927995
928- PYBIND11_NOINLINE static handle cast (const void *_src,
996+ static handle cast (const void *src,
997+ return_value_policy policy,
998+ handle parent,
999+ const detail::type_info *tinfo,
1000+ void *(*copy_constructor)(const void *),
1001+ void *(*move_constructor)(const void *),
1002+ const void *existing_holder = nullptr ) {
1003+ cast_sources srcs{src, tinfo};
1004+ return cast (srcs, policy, parent, copy_constructor, move_constructor, existing_holder);
1005+ }
1006+
1007+ PYBIND11_NOINLINE static handle cast (const cast_sources &srcs,
9291008 return_value_policy policy,
9301009 handle parent,
931- const detail::type_info *tinfo,
9321010 void *(*copy_constructor)(const void *),
9331011 void *(*move_constructor)(const void *),
9341012 const void *existing_holder = nullptr ) {
935- if (!tinfo) { // no type info: error will be set already
1013+ if (!srcs.result .tinfo ) {
1014+ // No pybind11 type info. Raise an exception.
1015+ std::string tname = srcs.downcast .cpptype ? srcs.downcast .cpptype ->name ()
1016+ : srcs.original .cpptype ? srcs.original .cpptype ->name ()
1017+ : " <unspecified>" ;
1018+ detail::clean_type_id (tname);
1019+ std::string msg = " Unregistered type : " + tname;
1020+ set_error (PyExc_TypeError, msg.c_str ());
9361021 return handle ();
9371022 }
9381023
939- void *src = const_cast <void *>(_src );
1024+ void *src = const_cast <void *>(srcs. result . cppobj );
9401025 if (src == nullptr ) {
9411026 return none ().release ();
9421027 }
1028+ const type_info *tinfo = srcs.result .tinfo ;
9431029
9441030 if (handle registered_inst = find_registered_python_instance (src, tinfo)) {
9451031 return registered_inst;
@@ -1212,25 +1298,6 @@ class type_caster_generic {
12121298 return false ;
12131299 }
12141300
1215- // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast
1216- // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair
1217- // with .second = nullptr. (p.first = nullptr is not an error: it becomes None).
1218- PYBIND11_NOINLINE static std::pair<const void *, const type_info *>
1219- src_and_type (const void *src,
1220- const std::type_info &cast_type,
1221- const std::type_info *rtti_type = nullptr ) {
1222- if (auto *tpi = get_type_info (cast_type)) {
1223- return {src, const_cast <const type_info *>(tpi)};
1224- }
1225-
1226- // Not found, set error:
1227- std::string tname = rtti_type ? rtti_type->name () : cast_type.name ();
1228- detail::clean_type_id (tname);
1229- std::string msg = " Unregistered type : " + tname;
1230- set_error (PyExc_TypeError, msg.c_str ());
1231- return {nullptr , nullptr };
1232- }
1233-
12341301 const type_info *typeinfo = nullptr ;
12351302 const std::type_info *cpptype = nullptr ;
12361303 void *value = nullptr ;
@@ -1525,6 +1592,20 @@ struct polymorphic_type_hook : public polymorphic_type_hook_base<itype> {};
15251592
15261593PYBIND11_NAMESPACE_BEGIN (detail)
15271594
1595+ template <typename itype>
1596+ cast_sources::cast_sources(const itype *ptr) : original{ptr, &typeid (itype)} {
1597+ // If this is a base pointer to a derived type, and the derived type is
1598+ // registered with pybind11, we want to make the full derived object
1599+ // available. In the typical case where itype is polymorphic, we get the
1600+ // correct derived pointer (which may be != base pointer) by a dynamic_cast
1601+ // to most derived type. If itype is not polymorphic, a user-provided
1602+ // specialization of polymorphic_type_hook can do the same thing.
1603+ // If there is no downcast to perform, then the default hook will leave
1604+ // derived.type set to nullptr, which causes us to ignore derived.obj.
1605+ downcast.cppobj = polymorphic_type_hook<itype>::get (ptr, downcast.cpptype );
1606+ result = resolve ();
1607+ }
1608+
15281609// / Generic type caster for objects stored on the heap
15291610template <typename type>
15301611class type_caster_base : public type_caster_generic {
@@ -1536,6 +1617,14 @@ class type_caster_base : public type_caster_generic {
15361617 type_caster_base () : type_caster_base(typeid (type)) {}
15371618 explicit type_caster_base (const std::type_info &info) : type_caster_generic(info) {}
15381619
1620+ // Wrap the generic cast_sources to be only constructible from the type
1621+ // that's correct in this context, so you can't use type_caster_base<A>
1622+ // to convert an unrelated B* to Python.
1623+ struct cast_sources : detail::cast_sources {
1624+ // NOLINTNEXTLINE(google-explicit-constructor)
1625+ cast_sources (const itype *ptr) : detail::cast_sources(ptr) {}
1626+ };
1627+
15391628 static handle cast (const itype &src, return_value_policy policy, handle parent) {
15401629 if (policy == return_value_policy::automatic
15411630 || policy == return_value_policy::automatic_reference) {
@@ -1548,50 +1637,17 @@ class type_caster_base : public type_caster_generic {
15481637 return cast (std::addressof (src), return_value_policy::move, parent);
15491638 }
15501639
1551- // Returns a (pointer, type_info) pair taking care of necessary type lookup for a
1552- // polymorphic type (using RTTI by default, but can be overridden by specializing
1553- // polymorphic_type_hook). If the instance isn't derived, returns the base version.
1554- static std::pair<const void *, const type_info *> src_and_type (const itype *src) {
1555- const auto &cast_type = typeid (itype);
1556- const std::type_info *instance_type = nullptr ;
1557- const void *vsrc = polymorphic_type_hook<itype>::get (src, instance_type);
1558- if (instance_type && !same_type (cast_type, *instance_type)) {
1559- // This is a base pointer to a derived type. If the derived type is registered
1560- // with pybind11, we want to make the full derived object available.
1561- // In the typical case where itype is polymorphic, we get the correct
1562- // derived pointer (which may be != base pointer) by a dynamic_cast to
1563- // most derived type. If itype is not polymorphic, we won't get here
1564- // except via a user-provided specialization of polymorphic_type_hook,
1565- // and the user has promised that no this-pointer adjustment is
1566- // required in that case, so it's OK to use static_cast.
1567- if (const auto *tpi = get_type_info (*instance_type)) {
1568- return {vsrc, tpi};
1569- }
1570- }
1571- // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer,
1572- // so don't do a cast
1573- return type_caster_generic::src_and_type (src, cast_type, instance_type);
1574- }
1575-
1576- static handle cast (const itype *src, return_value_policy policy, handle parent) {
1577- auto st = src_and_type (src);
1578- return type_caster_generic::cast (st.first ,
1640+ static handle cast (const cast_sources &srcs, return_value_policy policy, handle parent) {
1641+ return type_caster_generic::cast (srcs,
15791642 policy,
15801643 parent,
1581- st.second ,
1582- make_copy_constructor (src),
1583- make_move_constructor (src));
1584- }
1585-
1586- static handle cast_holder (const itype *src, const void *holder) {
1587- auto st = src_and_type (src);
1588- return type_caster_generic::cast (st.first ,
1589- return_value_policy::take_ownership,
1590- {},
1591- st.second ,
1592- nullptr ,
1593- nullptr ,
1594- holder);
1644+ make_copy_constructor ((const itype *) nullptr ),
1645+ make_move_constructor ((const itype *) nullptr ));
1646+ }
1647+
1648+ static handle cast_holder (const cast_sources &srcs, const void *holder) {
1649+ auto policy = return_value_policy::take_ownership;
1650+ return type_caster_generic::cast (srcs, policy, {}, nullptr , nullptr , holder);
15951651 }
15961652
15971653 template <typename T>
0 commit comments