diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index da81c0d884..15d62086e0 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -243,17 +243,17 @@ struct type_record { /// Is the default (unique_ptr) holder type used? bool default_holder : 1; - PYBIND11_NOINLINE void add_base(const std::type_info *base, void *(*caster)(void *)) { - auto base_info = detail::get_type_info(*base, false); + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { + auto base_info = detail::get_type_info(base, false); if (!base_info) { - std::string tname(base->name()); + std::string tname(base.name()); detail::clean_type_id(tname); pybind11_fail("generic_type: type \"" + std::string(name) + "\" referenced unknown base type \"" + tname + "\""); } if (default_holder != base_info->default_holder) { - std::string tname(base->name()); + std::string tname(base.name()); detail::clean_type_id(tname); pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + (default_holder ? "does not have" : "has") + @@ -384,7 +384,7 @@ struct process_attribute::value>> : process_attrib /// Process a parent class attribute (deprecated, does not support multiple inheritance) template struct process_attribute> : process_attribute_default> { - static void init(const base &, type_record *r) { r->add_base(&typeid(T), nullptr); } + static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } }; /// Process a multiple inheritance attribute diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index fbca3c61bd..d34afbc2a0 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -16,7 +16,6 @@ #include #include #include -#include #if defined(PYBIND11_CPP17) # if defined(__has_include) @@ -659,14 +658,14 @@ class type_caster_generic { // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). PYBIND11_NOINLINE static std::pair src_and_type( - const void *src, const std::type_info *cast_type, const std::type_info *rtti_type = nullptr) { + const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { auto &internals = get_internals(); - auto it = internals.registered_types_cpp.find(std::type_index(*cast_type)); + auto it = internals.registered_types_cpp.find(std::type_index(cast_type)); if (it != internals.registered_types_cpp.end()) return {src, (const type_info *) it->second}; // Not found, set error: - std::string tname = (rtti_type ? rtti_type : cast_type)->name(); + std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); detail::clean_type_id(tname); std::string msg = "Unregistered type : " + tname; PyErr_SetString(PyExc_TypeError, msg.c_str()); @@ -743,11 +742,11 @@ template class type_caster_base : public type_caster_generic { static std::pair src_and_type(const itype *src) { const void *vsrc = src; auto &internals = get_internals(); - auto cast_type = &typeid(itype); + auto &cast_type = typeid(itype); const std::type_info *instance_type = nullptr; if (vsrc) { instance_type = &typeid(*src); - if (instance_type != cast_type) { + if (!same_type(cast_type, *instance_type)) { // This is a base pointer to a derived type; if it is a pybind11-registered type, we // can get the correct derived pointer (which may be != base pointer) by a // dynamic_cast to most derived type: @@ -764,7 +763,7 @@ template class type_caster_base : public type_caster_generic { // Non-polymorphic type, so no dynamic casting; just call the generic version directly template ::value, int> = 0> static std::pair src_and_type(const itype *src) { - return type_caster_generic::src_and_type(src, &typeid(itype)); + return type_caster_generic::src_and_type(src, typeid(itype)); } static handle cast(const itype *src, return_value_policy policy, handle parent) { @@ -1739,7 +1738,7 @@ constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } NAMESPACE_BEGIN(detail) -// forward declaration +// forward declaration (definition in attr.h) struct function_record; /// Internal data associated with a single function call diff --git a/include/pybind11/common.h b/include/pybind11/common.h index b0d1bc22de..928205193f 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -124,6 +124,7 @@ #endif #include +#include #include #include #include @@ -426,13 +427,48 @@ struct overload_hash { } }; +// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly +// other stls, this means `typeid(A)` from one module won't equal `typeid(A)` from another module +// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under +// stdlibc++, this doesn't happen: equality and the type_index hash are based on the type name, +// which works. If not under a known-good stl, provide our own name-based hasher and equality +// functions that use the type name. +#if defined(__GLIBCXX__) +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } +using type_hash = std::hash; +using type_equal_to = std::equal_to; +#else +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { + return lhs.name() == rhs.name() || + std::strcmp(lhs.name(), rhs.name()) == 0; +} +struct type_hash { + size_t operator()(const std::type_index &t) const { + size_t hash = 5381; + const char *ptr = t.name(); + while (auto c = static_cast(*ptr++)) + hash = (hash * 33) ^ c; + return hash; + } +}; +struct type_equal_to { + bool operator()(const std::type_index &lhs, const std::type_index &rhs) const { + return lhs.name() == rhs.name() || + std::strcmp(lhs.name(), rhs.name()) == 0; + } +}; +#endif + +template +using type_map = std::unordered_map; + /// Internal data structure used to track registered instances and types struct internals { - std::unordered_map registered_types_cpp; // std::type_index -> type_info + type_map registered_types_cpp; // std::type_index -> type_info std::unordered_map> registered_types_py; // PyTypeObject* -> base type_info(s) std::unordered_multimap registered_instances; // void * -> instance* std::unordered_set, overload_hash> inactive_overload_cache; - std::unordered_map> direct_conversions; + type_map> direct_conversions; std::forward_list registered_exception_translators; std::unordered_map shared_data; // Custom data to be shared across extensions PyTypeObject *static_property_type; diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index ae168c4d8a..fdb6b330c9 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -46,7 +46,8 @@ struct type_caster> { auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); auto rec = (function_record *) c; - if (rec && rec->is_stateless && rec->data[1] == &typeid(function_type)) { + if (rec && rec->is_stateless && + same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { struct capture { function_type f; }; value = ((capture *) &rec->data)->f; return true; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 08268b8e87..40b32a83e9 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -968,7 +968,7 @@ class class_ : public detail::generic_type { template ::value, int> = 0> static void add_base(detail::type_record &rec) { - rec.add_base(&typeid(Base), [](void *src) -> void * { + rec.add_base(typeid(Base), [](void *src) -> void * { return static_cast(reinterpret_cast(src)); }); }