Skip to content

Use std::type_info::name() for type lookups outside stdlibc++ #915

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions include/pybind11/attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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") +
Expand Down Expand Up @@ -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)
template <typename 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
Expand Down
15 changes: 7 additions & 8 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#include <array>
#include <limits>
#include <tuple>
#include <cstring>

NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail)
Expand Down Expand Up @@ -646,14 +645,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<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 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());
Expand Down Expand Up @@ -730,11 +729,11 @@ template <typename type> class type_caster_base : public type_caster_generic {
static std::pair<const void *, const type_info *> 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:
Expand All @@ -751,7 +750,7 @@ template <typename type> class type_caster_base : public type_caster_generic {
// 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>
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) {
Expand Down Expand Up @@ -1700,7 +1699,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
Expand Down
40 changes: 38 additions & 2 deletions include/pybind11/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
#endif

#include <cstddef>
#include <cstring>
#include <forward_list>
#include <vector>
#include <string>
Expand Down Expand Up @@ -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
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_multimap<const void *, instance*> registered_instances; // void * -> instance*
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::unordered_map<std::string, void *> shared_data; // Custom data to be shared across extensions
PyTypeObject *static_property_type;
Expand Down
3 changes: 2 additions & 1 deletion include/pybind11/functional.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ struct type_caster<std::function<Return(Args...)>> {
auto c = reinterpret_borrow<capsule>(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<const std::type_info *>(rec->data[1]))) {
struct capture { function_type f; };
value = ((capture *) &rec->data)->f;
return true;
Expand Down
2 changes: 1 addition & 1 deletion include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ class class_ : public detail::generic_type {

template <typename Base, detail::enable_if_t<is_base<Base>::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<Base *>(reinterpret_cast<type *>(src));
});
}
Expand Down