mirror of https://github.com/pybind/pybind11.git
Use std::type_info::name() for type lookups outside stdlibc++
Using `std::type_info::operator==` fails under libc++ because the .so is loaded with RTLD_LOCAL. libc++ considers types under such .sos distinct, and so comparing typeid() values directly isn't going to work. This adds a custom hasher and equality class for the type lookup maps when not under stdlibc++, and adds a `detail::same_type` function to perform the equality test. It also converts a few pointer arguments to const lvalue references, particularly since doing the pointer comparison wasn't technically valid to being with (though in practice, appeared to work everywhere). This fixes #912.
This commit is contained in:
parent
2d6116b53f
commit
2196696746
|
@ -243,17 +243,17 @@ struct type_record {
|
||||||
/// Is the default (unique_ptr) holder type used?
|
/// Is the default (unique_ptr) holder type used?
|
||||||
bool default_holder : 1;
|
bool default_holder : 1;
|
||||||
|
|
||||||
PYBIND11_NOINLINE void add_base(const std::type_info *base, void *(*caster)(void *)) {
|
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) {
|
||||||
auto base_info = detail::get_type_info(*base, false);
|
auto base_info = detail::get_type_info(base, false);
|
||||||
if (!base_info) {
|
if (!base_info) {
|
||||||
std::string tname(base->name());
|
std::string tname(base.name());
|
||||||
detail::clean_type_id(tname);
|
detail::clean_type_id(tname);
|
||||||
pybind11_fail("generic_type: type \"" + std::string(name) +
|
pybind11_fail("generic_type: type \"" + std::string(name) +
|
||||||
"\" referenced unknown base type \"" + tname + "\"");
|
"\" referenced unknown base type \"" + tname + "\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (default_holder != base_info->default_holder) {
|
if (default_holder != base_info->default_holder) {
|
||||||
std::string tname(base->name());
|
std::string tname(base.name());
|
||||||
detail::clean_type_id(tname);
|
detail::clean_type_id(tname);
|
||||||
pybind11_fail("generic_type: type \"" + std::string(name) + "\" " +
|
pybind11_fail("generic_type: type \"" + std::string(name) + "\" " +
|
||||||
(default_holder ? "does not have" : "has") +
|
(default_holder ? "does not have" : "has") +
|
||||||
|
@ -384,7 +384,7 @@ struct process_attribute<T, enable_if_t<is_pyobject<T>::value>> : process_attrib
|
||||||
/// Process a parent class attribute (deprecated, does not support multiple inheritance)
|
/// Process a parent class attribute (deprecated, does not support multiple inheritance)
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct process_attribute<base<T>> : process_attribute_default<base<T>> {
|
struct process_attribute<base<T>> : process_attribute_default<base<T>> {
|
||||||
static void init(const base<T> &, type_record *r) { r->add_base(&typeid(T), nullptr); }
|
static void init(const base<T> &, type_record *r) { r->add_base(typeid(T), nullptr); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Process a multiple inheritance attribute
|
/// Process a multiple inheritance attribute
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#if defined(PYBIND11_CPP17)
|
#if defined(PYBIND11_CPP17)
|
||||||
# if defined(__has_include)
|
# if defined(__has_include)
|
||||||
|
@ -659,14 +658,14 @@ protected:
|
||||||
// isn't needed or can't be used. If the type is unknown, sets the error and returns a pair
|
// 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).
|
// with .second = nullptr. (p.first = nullptr is not an error: it becomes None).
|
||||||
PYBIND11_NOINLINE static std::pair<const void *, const type_info *> src_and_type(
|
PYBIND11_NOINLINE static std::pair<const void *, const type_info *> 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 &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())
|
if (it != internals.registered_types_cpp.end())
|
||||||
return {src, (const type_info *) it->second};
|
return {src, (const type_info *) it->second};
|
||||||
|
|
||||||
// Not found, set error:
|
// 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);
|
detail::clean_type_id(tname);
|
||||||
std::string msg = "Unregistered type : " + tname;
|
std::string msg = "Unregistered type : " + tname;
|
||||||
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
PyErr_SetString(PyExc_TypeError, msg.c_str());
|
||||||
|
@ -743,11 +742,11 @@ public:
|
||||||
static std::pair<const void *, const type_info *> src_and_type(const itype *src) {
|
static std::pair<const void *, const type_info *> src_and_type(const itype *src) {
|
||||||
const void *vsrc = src;
|
const void *vsrc = src;
|
||||||
auto &internals = get_internals();
|
auto &internals = get_internals();
|
||||||
auto cast_type = &typeid(itype);
|
auto &cast_type = typeid(itype);
|
||||||
const std::type_info *instance_type = nullptr;
|
const std::type_info *instance_type = nullptr;
|
||||||
if (vsrc) {
|
if (vsrc) {
|
||||||
instance_type = &typeid(*src);
|
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
|
// 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
|
// can get the correct derived pointer (which may be != base pointer) by a
|
||||||
// dynamic_cast to most derived type:
|
// dynamic_cast to most derived type:
|
||||||
|
@ -764,7 +763,7 @@ public:
|
||||||
// Non-polymorphic type, so no dynamic casting; just call the generic version directly
|
// Non-polymorphic type, so no dynamic casting; just call the generic version directly
|
||||||
template <typename T = itype, enable_if_t<!std::is_polymorphic<T>::value, int> = 0>
|
template <typename T = itype, enable_if_t<!std::is_polymorphic<T>::value, int> = 0>
|
||||||
static std::pair<const void *, const type_info *> src_and_type(const itype *src) {
|
static std::pair<const void *, const type_info *> 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) {
|
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)
|
NAMESPACE_BEGIN(detail)
|
||||||
|
|
||||||
// forward declaration
|
// forward declaration (definition in attr.h)
|
||||||
struct function_record;
|
struct function_record;
|
||||||
|
|
||||||
/// Internal data associated with a single function call
|
/// Internal data associated with a single function call
|
||||||
|
|
|
@ -124,6 +124,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
#include <forward_list>
|
#include <forward_list>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -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<std::type_index>;
|
||||||
|
using type_equal_to = std::equal_to<std::type_index>;
|
||||||
|
#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<unsigned char>(*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 <typename value_type>
|
||||||
|
using type_map = std::unordered_map<std::type_index, value_type, type_hash, type_equal_to>;
|
||||||
|
|
||||||
/// Internal data structure used to track registered instances and types
|
/// Internal data structure used to track registered instances and types
|
||||||
struct internals {
|
struct internals {
|
||||||
std::unordered_map<std::type_index, void*> registered_types_cpp; // std::type_index -> type_info
|
type_map<void *> registered_types_cpp; // std::type_index -> type_info
|
||||||
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; // PyTypeObject* -> base type_info(s)
|
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; // PyTypeObject* -> base type_info(s)
|
||||||
std::unordered_multimap<const void *, instance*> registered_instances; // void * -> instance*
|
std::unordered_multimap<const void *, instance*> registered_instances; // void * -> instance*
|
||||||
std::unordered_set<std::pair<const PyObject *, const char *>, overload_hash> inactive_overload_cache;
|
std::unordered_set<std::pair<const PyObject *, const char *>, overload_hash> inactive_overload_cache;
|
||||||
std::unordered_map<std::type_index, std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
|
type_map<std::vector<bool (*)(PyObject *, void *&)>> direct_conversions;
|
||||||
std::forward_list<void (*) (std::exception_ptr)> registered_exception_translators;
|
std::forward_list<void (*) (std::exception_ptr)> registered_exception_translators;
|
||||||
std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across extensions
|
std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across extensions
|
||||||
PyTypeObject *static_property_type;
|
PyTypeObject *static_property_type;
|
||||||
|
|
|
@ -46,7 +46,8 @@ public:
|
||||||
auto c = reinterpret_borrow<capsule>(PyCFunction_GET_SELF(cfunc.ptr()));
|
auto c = reinterpret_borrow<capsule>(PyCFunction_GET_SELF(cfunc.ptr()));
|
||||||
auto rec = (function_record *) c;
|
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<const std::type_info *>(rec->data[1]))) {
|
||||||
struct capture { function_type f; };
|
struct capture { function_type f; };
|
||||||
value = ((capture *) &rec->data)->f;
|
value = ((capture *) &rec->data)->f;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -968,7 +968,7 @@ public:
|
||||||
|
|
||||||
template <typename Base, detail::enable_if_t<is_base<Base>::value, int> = 0>
|
template <typename Base, detail::enable_if_t<is_base<Base>::value, int> = 0>
|
||||||
static void add_base(detail::type_record &rec) {
|
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<Base *>(reinterpret_cast<type *>(src));
|
return static_cast<Base *>(reinterpret_cast<type *>(src));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue