mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-29 00:22:00 +00:00
Support multiple inheritance from python
This commit allows multiple inheritance of pybind11 classes from Python, e.g. class MyType(Base1, Base2): def __init__(self): Base1.__init__(self) Base2.__init__(self) where Base1 and Base2 are pybind11-exported classes. This requires collapsing the various builtin base objects (pybind11_object_56, ...) introduced in 2.1 into a single pybind11_object of a fixed size; this fixed size object allocates enough space to contain either a simple object (one base class & small* holder instance), or a pointer to a new allocation that can contain an arbitrary number of base classes and holders, with holder size unrestricted. * "small" here means having a sizeof() of at most 2 pointers, which is enough to fit unique_ptr (sizeof is 1 ptr) and shared_ptr (sizeof is 2 ptrs). To minimize the performance impact, this repurposes `internals::registered_types_py` to store a vector of pybind-registered base types. For direct-use pybind types (e.g. the `PyA` for a C++ `A`) this is simply storing the same thing as before, but now in a vector; for Python-side inherited types, the map lets us avoid having to do a base class traversal as long as we've seen the class before. The change to vector is needed for multiple inheritance: Python types inheriting from multiple registered bases have one entry per base.
This commit is contained in:
parent
caedf74a89
commit
e45c211497
@ -619,27 +619,19 @@ interspersed with alias types and holder types (discussed earlier in this
|
|||||||
document)---pybind11 will automatically find out which is which. The only
|
document)---pybind11 will automatically find out which is which. The only
|
||||||
requirement is that the first template argument is the type to be declared.
|
requirement is that the first template argument is the type to be declared.
|
||||||
|
|
||||||
There are two caveats regarding the implementation of this feature:
|
It is also permitted to inherit multiply from exported C++ classes in Python,
|
||||||
|
as well as inheriting from multiple Python and/or pybind-exported classes.
|
||||||
|
|
||||||
1. When only one base type is specified for a C++ type that actually has
|
There is one caveat regarding the implementation of this feature:
|
||||||
multiple bases, pybind11 will assume that it does not participate in
|
|
||||||
multiple inheritance, which can lead to undefined behavior. In such cases,
|
When only one base type is specified for a C++ type that actually has multiple
|
||||||
add the tag ``multiple_inheritance``:
|
bases, pybind11 will assume that it does not participate in multiple
|
||||||
|
inheritance, which can lead to undefined behavior. In such cases, add the tag
|
||||||
|
``multiple_inheritance`` to the class constructor:
|
||||||
|
|
||||||
.. code-block:: cpp
|
.. code-block:: cpp
|
||||||
|
|
||||||
py::class_<MyType, BaseType2>(m, "MyType", py::multiple_inheritance());
|
py::class_<MyType, BaseType2>(m, "MyType", py::multiple_inheritance());
|
||||||
|
|
||||||
The tag is redundant and does not need to be specified when multiple base
|
The tag is redundant and does not need to be specified when multiple base types
|
||||||
types are listed.
|
are listed.
|
||||||
|
|
||||||
2. As was previously discussed in the section on :ref:`overriding_virtuals`, it
|
|
||||||
is easy to create Python types that derive from C++ classes. It is even
|
|
||||||
possible to make use of multiple inheritance to declare a Python class which
|
|
||||||
has e.g. a C++ and a Python class as bases. However, any attempt to create a
|
|
||||||
type that has *two or more* C++ classes in its hierarchy of base types will
|
|
||||||
fail with a fatal error message: ``TypeError: multiple bases have instance
|
|
||||||
lay-out conflict``. Core Python types that are implemented in C (e.g.
|
|
||||||
``dict``, ``list``, ``Exception``, etc.) also fall under this combination
|
|
||||||
and cannot be combined with C++ types bound using pybind11 via multiple
|
|
||||||
inheritance.
|
|
||||||
|
@ -210,17 +210,17 @@ struct type_record {
|
|||||||
/// How large is the underlying C++ type?
|
/// How large is the underlying C++ type?
|
||||||
size_t type_size = 0;
|
size_t type_size = 0;
|
||||||
|
|
||||||
/// How large is pybind11::instance<type>?
|
/// How large is the type's holder?
|
||||||
size_t instance_size = 0;
|
size_t holder_size = 0;
|
||||||
|
|
||||||
/// The global operator new can be overridden with a class-specific variant
|
/// The global operator new can be overridden with a class-specific variant
|
||||||
void *(*operator_new)(size_t) = ::operator new;
|
void *(*operator_new)(size_t) = ::operator new;
|
||||||
|
|
||||||
/// Function pointer to class_<..>::init_holder
|
/// Function pointer to class_<..>::init_holder
|
||||||
void (*init_holder)(PyObject *, const void *) = nullptr;
|
void (*init_holder)(instance *, const void *) = nullptr;
|
||||||
|
|
||||||
/// Function pointer to class_<..>::dealloc
|
/// Function pointer to class_<..>::dealloc
|
||||||
void (*dealloc)(PyObject *) = nullptr;
|
void (*dealloc)(const detail::value_and_holder &) = nullptr;
|
||||||
|
|
||||||
/// List of base classes of the newly created type
|
/// List of base classes of the newly created type
|
||||||
list bases;
|
list bases;
|
||||||
|
@ -16,20 +16,24 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
NAMESPACE_BEGIN(pybind11)
|
NAMESPACE_BEGIN(pybind11)
|
||||||
NAMESPACE_BEGIN(detail)
|
NAMESPACE_BEGIN(detail)
|
||||||
|
// Forward declarations:
|
||||||
inline PyTypeObject *make_static_property_type();
|
inline PyTypeObject *make_static_property_type();
|
||||||
inline PyTypeObject *make_default_metaclass();
|
inline PyTypeObject *make_default_metaclass();
|
||||||
|
inline PyObject *make_object_base_type(PyTypeObject *metaclass);
|
||||||
|
struct value_and_holder;
|
||||||
|
|
||||||
/// Additional type information which does not fit into the PyTypeObject
|
/// Additional type information which does not fit into the PyTypeObject
|
||||||
struct type_info {
|
struct type_info {
|
||||||
PyTypeObject *type;
|
PyTypeObject *type;
|
||||||
const std::type_info *cpptype;
|
const std::type_info *cpptype;
|
||||||
size_t type_size;
|
size_t type_size, holder_size_in_ptrs;
|
||||||
void *(*operator_new)(size_t);
|
void *(*operator_new)(size_t);
|
||||||
void (*init_holder)(PyObject *, const void *);
|
void (*init_holder)(instance *, const void *);
|
||||||
void (*dealloc)(PyObject *);
|
void (*dealloc)(const value_and_holder &v_h);
|
||||||
std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions;
|
std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions;
|
||||||
std::vector<std::pair<const std::type_info *, void *(*)(void *)>> implicit_casts;
|
std::vector<std::pair<const std::type_info *, void *(*)(void *)>> implicit_casts;
|
||||||
std::vector<bool (*)(PyObject *, void *&)> *direct_conversions;
|
std::vector<bool (*)(PyObject *, void *&)> *direct_conversions;
|
||||||
@ -90,20 +94,93 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
|||||||
);
|
);
|
||||||
internals_ptr->static_property_type = make_static_property_type();
|
internals_ptr->static_property_type = make_static_property_type();
|
||||||
internals_ptr->default_metaclass = make_default_metaclass();
|
internals_ptr->default_metaclass = make_default_metaclass();
|
||||||
|
internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass);
|
||||||
}
|
}
|
||||||
return *internals_ptr;
|
return *internals_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) {
|
// Gets the cache entry for the given type, creating it if necessary. The return value is the pair
|
||||||
|
// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was
|
||||||
|
// just created.
|
||||||
|
inline std::pair<decltype(internals::registered_types_py)::iterator, bool> all_type_info_get_cache(PyTypeObject *type);
|
||||||
|
|
||||||
|
// Populates a just-created cache entry.
|
||||||
|
PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector<type_info *> &bases) {
|
||||||
|
std::vector<PyTypeObject *> check;
|
||||||
|
for (handle parent : reinterpret_borrow<tuple>(t->tp_bases))
|
||||||
|
check.push_back((PyTypeObject *) parent.ptr());
|
||||||
|
|
||||||
auto const &type_dict = get_internals().registered_types_py;
|
auto const &type_dict = get_internals().registered_types_py;
|
||||||
do {
|
for (size_t i = 0; i < check.size(); i++) {
|
||||||
|
auto type = check[i];
|
||||||
|
// Ignore Python2 old-style class super type:
|
||||||
|
if (!PyType_Check((PyObject *) type)) continue;
|
||||||
|
|
||||||
|
// Check `type` in the current set of registered python types:
|
||||||
auto it = type_dict.find(type);
|
auto it = type_dict.find(type);
|
||||||
if (it != type_dict.end())
|
if (it != type_dict.end()) {
|
||||||
return (detail::type_info *) it->second;
|
// We found a cache entry for it, so it's either pybind-registered or has pre-computed
|
||||||
type = type->tp_base;
|
// pybind bases, but we have to make sure we haven't already seen the type(s) before: we
|
||||||
if (!type)
|
// want to follow Python/virtual C++ rules that there should only be one instance of a
|
||||||
|
// common base.
|
||||||
|
for (auto *tinfo : it->second) {
|
||||||
|
// NB: Could use a second set here, rather than doing a linear search, but since
|
||||||
|
// having a large number of immediate pybind11-registered types seems fairly
|
||||||
|
// unlikely, that probably isn't worthwhile.
|
||||||
|
bool found = false;
|
||||||
|
for (auto *known : bases) {
|
||||||
|
if (known == tinfo) { found = true; break; }
|
||||||
|
}
|
||||||
|
if (!found) bases.push_back(tinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type->tp_bases) {
|
||||||
|
// It's some python type, so keep follow its bases classes to look for one or more
|
||||||
|
// registered types
|
||||||
|
if (i + 1 == check.size()) {
|
||||||
|
// When we're at the end, we can pop off the current element to avoid growing
|
||||||
|
// `check` when adding just one base (which is typical--.e. when there is no
|
||||||
|
// multiple inheritance)
|
||||||
|
check.pop_back();
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
for (handle parent : reinterpret_borrow<tuple>(type->tp_bases))
|
||||||
|
check.push_back((PyTypeObject *) parent.ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will
|
||||||
|
* be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side
|
||||||
|
* derived class that uses single inheritance. Will contain as many types as required for a Python
|
||||||
|
* class that uses multiple inheritance to inherit (directly or indirectly) from multiple
|
||||||
|
* pybind-registered classes. Will be empty if neither the type nor any base classes are
|
||||||
|
* pybind-registered.
|
||||||
|
*
|
||||||
|
* The value is cached for the lifetime of the Python type.
|
||||||
|
*/
|
||||||
|
inline const std::vector<detail::type_info *> &all_type_info(PyTypeObject *type) {
|
||||||
|
auto ins = all_type_info_get_cache(type);
|
||||||
|
if (ins.second)
|
||||||
|
// New cache entry: populate it
|
||||||
|
all_type_info_populate(type, ins.first->second);
|
||||||
|
|
||||||
|
return ins.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any
|
||||||
|
* ancestors are pybind11-registered. Throws an exception if there are multiple bases--use
|
||||||
|
* `all_type_info` instead if you want to support multiple bases.
|
||||||
|
*/
|
||||||
|
PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) {
|
||||||
|
auto &bases = all_type_info(type);
|
||||||
|
if (bases.size() == 0)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} while (true);
|
if (bases.size() > 1)
|
||||||
|
pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases");
|
||||||
|
return bases.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_info &tp,
|
PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_info &tp,
|
||||||
@ -126,6 +203,178 @@ PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool t
|
|||||||
return handle(type_info ? ((PyObject *) type_info->type) : nullptr);
|
return handle(type_info ? ((PyObject *) type_info->type) : nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct value_and_holder {
|
||||||
|
instance *inst;
|
||||||
|
size_t index;
|
||||||
|
const detail::type_info *type;
|
||||||
|
void **vh;
|
||||||
|
|
||||||
|
value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) :
|
||||||
|
inst{i}, index{index}, type{type},
|
||||||
|
vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]}
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Used for past-the-end iterator
|
||||||
|
value_and_holder(size_t index) : index{index} {}
|
||||||
|
|
||||||
|
template <typename V = void> V *&value_ptr() const {
|
||||||
|
return reinterpret_cast<V *&>(vh[0]);
|
||||||
|
}
|
||||||
|
// True if this `value_and_holder` has a non-null value pointer
|
||||||
|
explicit operator bool() const { return value_ptr(); }
|
||||||
|
|
||||||
|
template <typename H> H &holder() const {
|
||||||
|
return reinterpret_cast<H &>(vh[1]);
|
||||||
|
}
|
||||||
|
bool holder_constructed() const {
|
||||||
|
return inst->simple_layout
|
||||||
|
? inst->simple_holder_constructed
|
||||||
|
: inst->nonsimple.holder_constructed[index];
|
||||||
|
}
|
||||||
|
void set_holder_constructed() {
|
||||||
|
if (inst->simple_layout)
|
||||||
|
inst->simple_holder_constructed = true;
|
||||||
|
else
|
||||||
|
inst->nonsimple.holder_constructed[index] = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Container for accessing and iterating over an instance's values/holders
|
||||||
|
struct values_and_holders {
|
||||||
|
private:
|
||||||
|
instance *inst;
|
||||||
|
using type_vec = std::vector<detail::type_info *>;
|
||||||
|
const type_vec &tinfo;
|
||||||
|
|
||||||
|
public:
|
||||||
|
values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {}
|
||||||
|
|
||||||
|
struct iterator {
|
||||||
|
private:
|
||||||
|
instance *inst;
|
||||||
|
using vec_iter = std::vector<detail::type_info *>::const_iterator;
|
||||||
|
vec_iter typeit;
|
||||||
|
value_and_holder curr;
|
||||||
|
friend struct values_and_holders;
|
||||||
|
iterator(instance *inst, const type_vec &tinfo)
|
||||||
|
: inst{inst}, typeit{tinfo.begin()},
|
||||||
|
curr(inst /* instance */,
|
||||||
|
tinfo.size() > 0 ? *typeit : nullptr /* type info */,
|
||||||
|
0, /* vpos: (non-simple types only): the first vptr comes first */
|
||||||
|
0 /* index */)
|
||||||
|
{}
|
||||||
|
// Past-the-end iterator:
|
||||||
|
iterator(size_t end) : curr(end) {}
|
||||||
|
public:
|
||||||
|
bool operator==(const iterator &other) { return curr.index == other.curr.index; }
|
||||||
|
bool operator!=(const iterator &other) { return curr.index != other.curr.index; }
|
||||||
|
iterator &operator++() {
|
||||||
|
if (!inst->simple_layout) {
|
||||||
|
curr.vh += 1 + (*typeit)->holder_size_in_ptrs;
|
||||||
|
curr.type = *(++typeit);
|
||||||
|
}
|
||||||
|
++curr.index;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
value_and_holder &operator*() { return curr; }
|
||||||
|
value_and_holder *operator->() { return &curr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() { return iterator(inst, tinfo); }
|
||||||
|
iterator end() { return iterator(tinfo.size()); }
|
||||||
|
|
||||||
|
iterator find(const type_info *find_type) {
|
||||||
|
auto it = begin(), endit = end();
|
||||||
|
while (it != endit && it->type != find_type) ++it;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() { return tinfo.size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts C++ value and holder pointer references from an instance (which may contain multiple
|
||||||
|
* values/holders for python-side multiple inheritance) that match the given type. Throws an error
|
||||||
|
* if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If
|
||||||
|
* `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned,
|
||||||
|
* regardless of type (and the resulting .type will be nullptr).
|
||||||
|
*
|
||||||
|
* The returned object should be short-lived: in particular, it must not outlive the called-upon
|
||||||
|
* instance.
|
||||||
|
*/
|
||||||
|
PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/) {
|
||||||
|
// Optimize common case:
|
||||||
|
if (!find_type || Py_TYPE(this) == find_type->type)
|
||||||
|
return value_and_holder(this, find_type, 0, 0);
|
||||||
|
|
||||||
|
detail::values_and_holders vhs(this);
|
||||||
|
auto it = vhs.find(find_type);
|
||||||
|
if (it != vhs.end())
|
||||||
|
return *it;
|
||||||
|
|
||||||
|
#if defined(NDEBUG)
|
||||||
|
pybind11_fail("pybind11::detail::instance::get_value_and_holder: "
|
||||||
|
"type is not a pybind11 base of the given instance "
|
||||||
|
"(compile in debug mode for type details)");
|
||||||
|
#else
|
||||||
|
pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" +
|
||||||
|
std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" +
|
||||||
|
std::string(Py_TYPE(this)->tp_name) + "' instance");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NOINLINE inline void instance::allocate_layout() {
|
||||||
|
auto &tinfo = all_type_info(Py_TYPE(this));
|
||||||
|
|
||||||
|
const size_t n_types = tinfo.size();
|
||||||
|
|
||||||
|
if (n_types == 0)
|
||||||
|
pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types");
|
||||||
|
|
||||||
|
simple_layout =
|
||||||
|
n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs();
|
||||||
|
|
||||||
|
// Simple path: no python-side multiple inheritance, and a small-enough holder
|
||||||
|
if (simple_layout) {
|
||||||
|
simple_value_holder[0] = nullptr;
|
||||||
|
simple_holder_constructed = false;
|
||||||
|
}
|
||||||
|
else { // multiple base types or a too-large holder
|
||||||
|
// Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer,
|
||||||
|
// [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool
|
||||||
|
// values that tracks whether each associated holder has been initialized. Each [block] is
|
||||||
|
// padded, if necessary, to an integer multiple of sizeof(void *).
|
||||||
|
size_t space = 0;
|
||||||
|
for (auto t : tinfo) {
|
||||||
|
space += 1; // value pointer
|
||||||
|
space += t->holder_size_in_ptrs; // holder instance
|
||||||
|
}
|
||||||
|
size_t flags_at = space;
|
||||||
|
space += size_in_ptrs(n_types * sizeof(bool)); // holder constructed flags
|
||||||
|
|
||||||
|
// Allocate space for flags, values, and holders, and initialize it to 0 (flags and values,
|
||||||
|
// in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6
|
||||||
|
// they default to using pymalloc, which is designed to be efficient for small allocations
|
||||||
|
// like the one we're doing here; in earlier versions (and for larger allocations) they are
|
||||||
|
// just wrappers around malloc.
|
||||||
|
#if PY_VERSION_HEX >= 0x03050000
|
||||||
|
nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *));
|
||||||
|
if (!nonsimple.values_and_holders) throw std::bad_alloc();
|
||||||
|
#else
|
||||||
|
nonsimple.values_and_holders = (void **) PyMem_New(void *, space);
|
||||||
|
if (!nonsimple.values_and_holders) throw std::bad_alloc();
|
||||||
|
std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *));
|
||||||
|
#endif
|
||||||
|
nonsimple.holder_constructed = reinterpret_cast<bool *>(&nonsimple.values_and_holders[flags_at]);
|
||||||
|
}
|
||||||
|
owned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_NOINLINE inline void instance::deallocate_layout() {
|
||||||
|
if (!simple_layout)
|
||||||
|
PyMem_Free(nonsimple.values_and_holders);
|
||||||
|
}
|
||||||
|
|
||||||
PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) {
|
PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) {
|
||||||
handle type = detail::get_type_handle(tp, false);
|
handle type = detail::get_type_handle(tp, false);
|
||||||
if (!type)
|
if (!type)
|
||||||
@ -185,10 +434,11 @@ PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail:
|
|||||||
auto &instances = get_internals().registered_instances;
|
auto &instances = get_internals().registered_instances;
|
||||||
auto range = instances.equal_range(ptr);
|
auto range = instances.equal_range(ptr);
|
||||||
for (auto it = range.first; it != range.second; ++it) {
|
for (auto it = range.first; it != range.second; ++it) {
|
||||||
auto instance_type = detail::get_type_info(Py_TYPE(it->second));
|
for (auto vh : values_and_holders(it->second)) {
|
||||||
if (instance_type && instance_type == type)
|
if (vh.type == type)
|
||||||
return handle((PyObject *) it->second);
|
return handle((PyObject *) it->second);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return handle();
|
return handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,7 +458,7 @@ inline PyThreadState *get_thread_state_unchecked() {
|
|||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
inline void keep_alive_impl(handle nurse, handle patient);
|
inline void keep_alive_impl(handle nurse, handle patient);
|
||||||
inline void register_instance(void *self, const type_info *tinfo);
|
inline void register_instance(instance *self, void *valptr, const type_info *tinfo);
|
||||||
inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value = true);
|
inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value = true);
|
||||||
|
|
||||||
class type_caster_generic {
|
class type_caster_generic {
|
||||||
@ -216,70 +466,8 @@ public:
|
|||||||
PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info)
|
PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info)
|
||||||
: typeinfo(get_type_info(type_info)) { }
|
: typeinfo(get_type_info(type_info)) { }
|
||||||
|
|
||||||
PYBIND11_NOINLINE bool load(handle src, bool convert) {
|
bool load(handle src, bool convert) {
|
||||||
if (!src)
|
return load_impl<type_caster_generic>(src, convert);
|
||||||
return false;
|
|
||||||
return load(src, convert, Py_TYPE(src.ptr()));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool load(handle src, bool convert, PyTypeObject *tobj) {
|
|
||||||
if (!src || !typeinfo)
|
|
||||||
return false;
|
|
||||||
if (src.is_none()) {
|
|
||||||
// Defer accepting None to other overloads (if we aren't in convert mode):
|
|
||||||
if (!convert) return false;
|
|
||||||
value = nullptr;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeinfo->simple_type) { /* Case 1: no multiple inheritance etc. involved */
|
|
||||||
/* Check if we can safely perform a reinterpret-style cast */
|
|
||||||
if (PyType_IsSubtype(tobj, typeinfo->type)) {
|
|
||||||
value = reinterpret_cast<instance<void> *>(src.ptr())->value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else { /* Case 2: multiple inheritance */
|
|
||||||
/* Check if we can safely perform a reinterpret-style cast */
|
|
||||||
if (tobj == typeinfo->type) {
|
|
||||||
value = reinterpret_cast<instance<void> *>(src.ptr())->value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If this is a python class, also check the parents recursively */
|
|
||||||
auto const &type_dict = get_internals().registered_types_py;
|
|
||||||
bool new_style_class = PyType_Check((PyObject *) tobj);
|
|
||||||
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
|
|
||||||
auto parents = reinterpret_borrow<tuple>(tobj->tp_bases);
|
|
||||||
for (handle parent : parents) {
|
|
||||||
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
|
|
||||||
if (result)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try implicit casts */
|
|
||||||
for (auto &cast : typeinfo->implicit_casts) {
|
|
||||||
type_caster_generic sub_caster(*cast.first);
|
|
||||||
if (sub_caster.load(src, convert)) {
|
|
||||||
value = cast.second(sub_caster.value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform an implicit conversion */
|
|
||||||
if (convert) {
|
|
||||||
for (auto &converter : typeinfo->implicit_conversions) {
|
|
||||||
temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
|
|
||||||
if (load(temp, false))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (auto &converter : *typeinfo->direct_conversions) {
|
|
||||||
if (converter(src.ptr(), value))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent,
|
PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent,
|
||||||
@ -296,34 +484,33 @@ public:
|
|||||||
|
|
||||||
auto it_instances = get_internals().registered_instances.equal_range(src);
|
auto it_instances = get_internals().registered_instances.equal_range(src);
|
||||||
for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
|
for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) {
|
||||||
auto instance_type = detail::get_type_info(Py_TYPE(it_i->second));
|
for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) {
|
||||||
if (instance_type && instance_type == tinfo)
|
if (instance_type && instance_type == tinfo)
|
||||||
return handle((PyObject *) it_i->second).inc_ref();
|
return handle((PyObject *) it_i->second).inc_ref();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type, false /* don't allocate value */));
|
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type, false /* don't allocate value */));
|
||||||
|
auto wrapper = reinterpret_cast<instance *>(inst.ptr());
|
||||||
auto wrapper = (instance<void> *) inst.ptr();
|
|
||||||
|
|
||||||
wrapper->value = nullptr;
|
|
||||||
wrapper->owned = false;
|
wrapper->owned = false;
|
||||||
|
void *&valueptr = values_and_holders(wrapper).begin()->value_ptr();
|
||||||
|
|
||||||
switch (policy) {
|
switch (policy) {
|
||||||
case return_value_policy::automatic:
|
case return_value_policy::automatic:
|
||||||
case return_value_policy::take_ownership:
|
case return_value_policy::take_ownership:
|
||||||
wrapper->value = src;
|
valueptr = src;
|
||||||
wrapper->owned = true;
|
wrapper->owned = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case return_value_policy::automatic_reference:
|
case return_value_policy::automatic_reference:
|
||||||
case return_value_policy::reference:
|
case return_value_policy::reference:
|
||||||
wrapper->value = src;
|
valueptr = src;
|
||||||
wrapper->owned = false;
|
wrapper->owned = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case return_value_policy::copy:
|
case return_value_policy::copy:
|
||||||
if (copy_constructor)
|
if (copy_constructor)
|
||||||
wrapper->value = copy_constructor(src);
|
valueptr = copy_constructor(src);
|
||||||
else
|
else
|
||||||
throw cast_error("return_value_policy = copy, but the "
|
throw cast_error("return_value_policy = copy, but the "
|
||||||
"object is non-copyable!");
|
"object is non-copyable!");
|
||||||
@ -332,9 +519,9 @@ public:
|
|||||||
|
|
||||||
case return_value_policy::move:
|
case return_value_policy::move:
|
||||||
if (move_constructor)
|
if (move_constructor)
|
||||||
wrapper->value = move_constructor(src);
|
valueptr = move_constructor(src);
|
||||||
else if (copy_constructor)
|
else if (copy_constructor)
|
||||||
wrapper->value = copy_constructor(src);
|
valueptr = copy_constructor(src);
|
||||||
else
|
else
|
||||||
throw cast_error("return_value_policy = move, but the "
|
throw cast_error("return_value_policy = move, but the "
|
||||||
"object is neither movable nor copyable!");
|
"object is neither movable nor copyable!");
|
||||||
@ -342,23 +529,119 @@ public:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case return_value_policy::reference_internal:
|
case return_value_policy::reference_internal:
|
||||||
wrapper->value = src;
|
valueptr = src;
|
||||||
wrapper->owned = false;
|
wrapper->owned = false;
|
||||||
detail::keep_alive_impl(inst, parent);
|
keep_alive_impl(inst, parent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw cast_error("unhandled return_value_policy: should not happen!");
|
throw cast_error("unhandled return_value_policy: should not happen!");
|
||||||
}
|
}
|
||||||
|
|
||||||
register_instance(wrapper, tinfo);
|
register_instance(wrapper, valueptr, tinfo);
|
||||||
tinfo->init_holder(inst.ptr(), existing_holder);
|
tinfo->init_holder(wrapper, existing_holder);
|
||||||
|
|
||||||
return inst.release();
|
return inst.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
// Base methods for generic caster; there are overridden in copyable_holder_caster
|
||||||
|
void load_value(const value_and_holder &v_h) {
|
||||||
|
value = v_h.value_ptr();
|
||||||
|
}
|
||||||
|
bool try_implicit_casts(handle src, bool convert) {
|
||||||
|
for (auto &cast : typeinfo->implicit_casts) {
|
||||||
|
type_caster_generic sub_caster(*cast.first);
|
||||||
|
if (sub_caster.load(src, convert)) {
|
||||||
|
value = cast.second(sub_caster.value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool try_direct_conversions(handle src) {
|
||||||
|
for (auto &converter : *typeinfo->direct_conversions) {
|
||||||
|
if (converter(src.ptr(), value))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void check_holder_compat() {}
|
||||||
|
|
||||||
|
// Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant
|
||||||
|
// bits of code between here and copyable_holder_caster where the two classes need different
|
||||||
|
// logic (without having to resort to virtual inheritance).
|
||||||
|
template <typename ThisT>
|
||||||
|
PYBIND11_NOINLINE bool load_impl(handle src, bool convert) {
|
||||||
|
if (!src || !typeinfo)
|
||||||
|
return false;
|
||||||
|
if (src.is_none()) {
|
||||||
|
// Defer accepting None to other overloads (if we aren't in convert mode):
|
||||||
|
if (!convert) return false;
|
||||||
|
value = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &this_ = static_cast<ThisT &>(*this);
|
||||||
|
this_.check_holder_compat();
|
||||||
|
|
||||||
|
PyTypeObject *srctype = Py_TYPE(src.ptr());
|
||||||
|
|
||||||
|
// Case 1: If src is an exact type match for the target type then we can reinterpret_cast
|
||||||
|
// the instance's value pointer to the target type:
|
||||||
|
if (srctype == typeinfo->type) {
|
||||||
|
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Case 2: We have a derived class
|
||||||
|
else if (PyType_IsSubtype(srctype, typeinfo->type)) {
|
||||||
|
auto &bases = all_type_info(srctype);
|
||||||
|
bool no_cpp_mi = typeinfo->simple_type;
|
||||||
|
|
||||||
|
// Case 2a: the python type is a Python-inherited derived class that inherits from just
|
||||||
|
// one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of
|
||||||
|
// the right type and we can use reinterpret_cast.
|
||||||
|
// (This is essentially the same as case 2b, but because not using multiple inheritance
|
||||||
|
// is extremely common, we handle it specially to avoid the loop iterator and type
|
||||||
|
// pointer lookup overhead)
|
||||||
|
if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) {
|
||||||
|
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if
|
||||||
|
// we can find an exact match (or, for a simple C++ type, an inherited match); if so, we
|
||||||
|
// can safely reinterpret_cast to the relevant pointer.
|
||||||
|
else if (bases.size() > 1) {
|
||||||
|
for (auto base : bases) {
|
||||||
|
if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) {
|
||||||
|
this_.load_value(reinterpret_cast<instance *>(src.ptr())->get_value_and_holder(base));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match
|
||||||
|
// in the registered bases, above, so try implicit casting (needed for proper C++ casting
|
||||||
|
// when MI is involved).
|
||||||
|
if (this_.try_implicit_casts(src, convert))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform an implicit conversion
|
||||||
|
if (convert) {
|
||||||
|
for (auto &converter : typeinfo->implicit_conversions) {
|
||||||
|
temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
|
||||||
|
if (load_impl<ThisT>(temp, false))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this_.try_direct_conversions(src))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast
|
// Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast
|
||||||
// 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).
|
||||||
@ -669,8 +952,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check if this is a C++ type */
|
/* Check if this is a C++ type */
|
||||||
if (get_type_info((PyTypeObject *) h.get_type().ptr())) {
|
auto &bases = all_type_info((PyTypeObject *) h.get_type().ptr());
|
||||||
value = ((instance<void> *) h.ptr())->value;
|
if (bases.size() == 1) { // Only allowing loading from a single-value type
|
||||||
|
value = values_and_holders(reinterpret_cast<instance *>(h.ptr())).begin()->value_ptr();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1016,64 +1300,38 @@ public:
|
|||||||
using base::value;
|
using base::value;
|
||||||
using base::temp;
|
using base::temp;
|
||||||
|
|
||||||
PYBIND11_NOINLINE bool load(handle src, bool convert) {
|
bool load(handle src, bool convert) {
|
||||||
return load(src, convert, Py_TYPE(src.ptr()));
|
return base::template load_impl<copyable_holder_caster<type, holder_type>>(src, convert);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool load(handle src, bool convert, PyTypeObject *tobj) {
|
explicit operator type*() { return this->value; }
|
||||||
if (!src || !typeinfo)
|
explicit operator type&() { return *(this->value); }
|
||||||
return false;
|
explicit operator holder_type*() { return &holder; }
|
||||||
if (src.is_none()) {
|
|
||||||
// Defer accepting None to other overloads (if we aren't in convert mode):
|
// Workaround for Intel compiler bug
|
||||||
if (!convert) return false;
|
// see pybind11 issue 94
|
||||||
value = nullptr;
|
#if defined(__ICC) || defined(__INTEL_COMPILER)
|
||||||
return true;
|
operator holder_type&() { return holder; }
|
||||||
|
#else
|
||||||
|
explicit operator holder_type&() { return holder; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static handle cast(const holder_type &src, return_value_policy, handle) {
|
||||||
|
const auto *ptr = holder_helper<holder_type>::get(src);
|
||||||
|
return type_caster_base<type>::cast_holder(ptr, &src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class type_caster_generic;
|
||||||
|
void check_holder_compat() {
|
||||||
if (typeinfo->default_holder)
|
if (typeinfo->default_holder)
|
||||||
throw cast_error("Unable to load a custom holder type from a default-holder instance");
|
throw cast_error("Unable to load a custom holder type from a default-holder instance");
|
||||||
|
|
||||||
if (typeinfo->simple_type) { /* Case 1: no multiple inheritance etc. involved */
|
|
||||||
/* Check if we can safely perform a reinterpret-style cast */
|
|
||||||
if (PyType_IsSubtype(tobj, typeinfo->type))
|
|
||||||
return load_value_and_holder(src);
|
|
||||||
} else { /* Case 2: multiple inheritance */
|
|
||||||
/* Check if we can safely perform a reinterpret-style cast */
|
|
||||||
if (tobj == typeinfo->type)
|
|
||||||
return load_value_and_holder(src);
|
|
||||||
|
|
||||||
/* If this is a python class, also check the parents recursively */
|
|
||||||
auto const &type_dict = get_internals().registered_types_py;
|
|
||||||
bool new_style_class = PyType_Check((PyObject *) tobj);
|
|
||||||
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
|
|
||||||
auto parents = reinterpret_borrow<tuple>(tobj->tp_bases);
|
|
||||||
for (handle parent : parents) {
|
|
||||||
bool result = load(src, convert, (PyTypeObject *) parent.ptr());
|
|
||||||
if (result)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try_implicit_casts(src, convert))
|
bool load_value(const value_and_holder &v_h) {
|
||||||
return true;
|
if (v_h.holder_constructed()) {
|
||||||
}
|
value = v_h.value_ptr();
|
||||||
|
holder = v_h.holder<holder_type>();
|
||||||
if (convert) {
|
|
||||||
for (auto &converter : typeinfo->implicit_conversions) {
|
|
||||||
temp = reinterpret_steal<object>(converter(src.ptr(), typeinfo->type));
|
|
||||||
if (load(temp, false))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool load_value_and_holder(handle src) {
|
|
||||||
auto inst = (instance<type, holder_type> *) src.ptr();
|
|
||||||
value = (void *) inst->value;
|
|
||||||
if (inst->holder_constructed) {
|
|
||||||
holder = inst->holder;
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
|
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
|
||||||
@ -1101,24 +1359,9 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit operator type*() { return this->value; }
|
static constexpr bool try_direct_conversions(handle) { return false; }
|
||||||
explicit operator type&() { return *(this->value); }
|
|
||||||
explicit operator holder_type*() { return &holder; }
|
|
||||||
|
|
||||||
// Workaround for Intel compiler bug
|
|
||||||
// see pybind11 issue 94
|
|
||||||
#if defined(__ICC) || defined(__INTEL_COMPILER)
|
|
||||||
operator holder_type&() { return holder; }
|
|
||||||
#else
|
|
||||||
explicit operator holder_type&() { return holder; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static handle cast(const holder_type &src, return_value_policy, handle) {
|
|
||||||
const auto *ptr = holder_helper<holder_type>::get(src);
|
|
||||||
return type_caster_base<type>::cast_holder(ptr, &src);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
holder_type holder;
|
holder_type holder;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,34 +87,6 @@ inline PyTypeObject *make_static_property_type() {
|
|||||||
|
|
||||||
#endif // PYPY
|
#endif // PYPY
|
||||||
|
|
||||||
/** Inheriting from multiple C++ types in Python is not supported -- set an error instead.
|
|
||||||
A Python definition (`class C(A, B): pass`) will call `tp_new` so we check for multiple
|
|
||||||
C++ bases here. On the other hand, C++ type definitions (`py::class_<C, A, B>(m, "C")`)
|
|
||||||
don't not use `tp_new` and will not trigger this error. */
|
|
||||||
extern "C" inline PyObject *pybind11_meta_new(PyTypeObject *metaclass, PyObject *args,
|
|
||||||
PyObject *kwargs) {
|
|
||||||
PyObject *bases = PyTuple_GetItem(args, 1); // arguments: (name, bases, dict)
|
|
||||||
if (!bases)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
auto &internals = get_internals();
|
|
||||||
auto num_cpp_bases = 0;
|
|
||||||
for (auto base : reinterpret_borrow<tuple>(bases)) {
|
|
||||||
auto base_type = (PyTypeObject *) base.ptr();
|
|
||||||
auto instance_size = static_cast<size_t>(base_type->tp_basicsize);
|
|
||||||
if (PyObject_IsSubclass(base.ptr(), internals.get_base(instance_size)))
|
|
||||||
++num_cpp_bases;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num_cpp_bases > 1) {
|
|
||||||
PyErr_SetString(PyExc_TypeError, "Can't inherit from multiple C++ classes in Python."
|
|
||||||
"Use py::class_ to define the class in C++ instead.");
|
|
||||||
return nullptr;
|
|
||||||
} else {
|
|
||||||
return PyType_Type.tp_new(metaclass, args, kwargs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Types with static properties need to handle `Type.static_prop = x` in a specific way.
|
/** Types with static properties need to handle `Type.static_prop = x` in a specific way.
|
||||||
By default, Python replaces the `static_property` itself, but for wrapped C++ types
|
By default, Python replaces the `static_property` itself, but for wrapped C++ types
|
||||||
we need to call `static_property.__set__()` in order to propagate the new value to
|
we need to call `static_property.__set__()` in order to propagate the new value to
|
||||||
@ -193,7 +165,6 @@ inline PyTypeObject* make_default_metaclass() {
|
|||||||
type->tp_base = &PyType_Type;
|
type->tp_base = &PyType_Type;
|
||||||
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
||||||
|
|
||||||
type->tp_new = pybind11_meta_new;
|
|
||||||
type->tp_setattro = pybind11_meta_setattro;
|
type->tp_setattro = pybind11_meta_setattro;
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
type->tp_getattro = pybind11_meta_getattro;
|
type->tp_getattro = pybind11_meta_getattro;
|
||||||
@ -210,8 +181,8 @@ inline PyTypeObject* make_default_metaclass() {
|
|||||||
/// For multiple inheritance types we need to recursively register/deregister base pointers for any
|
/// For multiple inheritance types we need to recursively register/deregister base pointers for any
|
||||||
/// base classes with pointers that are difference from the instance value pointer so that we can
|
/// base classes with pointers that are difference from the instance value pointer so that we can
|
||||||
/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs.
|
/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs.
|
||||||
inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, void *self,
|
inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self,
|
||||||
bool (*f)(void * /*parentptr*/, void * /*self*/)) {
|
bool (*f)(void * /*parentptr*/, instance * /*self*/)) {
|
||||||
for (handle h : reinterpret_borrow<tuple>(tinfo->type->tp_bases)) {
|
for (handle h : reinterpret_borrow<tuple>(tinfo->type->tp_bases)) {
|
||||||
if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) {
|
if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) {
|
||||||
for (auto &c : parent_tinfo->implicit_casts) {
|
for (auto &c : parent_tinfo->implicit_casts) {
|
||||||
@ -227,11 +198,11 @@ inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool register_instance_impl(void *ptr, void *self) {
|
inline bool register_instance_impl(void *ptr, instance *self) {
|
||||||
get_internals().registered_instances.emplace(ptr, self);
|
get_internals().registered_instances.emplace(ptr, self);
|
||||||
return true; // unused, but gives the same signature as the deregister func
|
return true; // unused, but gives the same signature as the deregister func
|
||||||
}
|
}
|
||||||
inline bool deregister_instance_impl(void *ptr, void *self) {
|
inline bool deregister_instance_impl(void *ptr, instance *self) {
|
||||||
auto ®istered_instances = get_internals().registered_instances;
|
auto ®istered_instances = get_internals().registered_instances;
|
||||||
auto range = registered_instances.equal_range(ptr);
|
auto range = registered_instances.equal_range(ptr);
|
||||||
for (auto it = range.first; it != range.second; ++it) {
|
for (auto it = range.first; it != range.second; ++it) {
|
||||||
@ -243,36 +214,48 @@ inline bool deregister_instance_impl(void *ptr, void *self) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void register_instance(void *self, const type_info *tinfo) {
|
inline void register_instance(instance *self, void *valptr, const type_info *tinfo) {
|
||||||
auto *inst = (instance_essentials<void> *) self;
|
register_instance_impl(valptr, self);
|
||||||
register_instance_impl(inst->value, self);
|
|
||||||
if (!tinfo->simple_ancestors)
|
if (!tinfo->simple_ancestors)
|
||||||
traverse_offset_bases(inst->value, tinfo, self, register_instance_impl);
|
traverse_offset_bases(valptr, tinfo, self, register_instance_impl);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool deregister_instance(void *self, const detail::type_info *tinfo) {
|
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) {
|
||||||
auto *inst = (instance_essentials<void> *) self;
|
bool ret = deregister_instance_impl(valptr, self);
|
||||||
bool ret = deregister_instance_impl(inst->value, self);
|
|
||||||
if (!tinfo->simple_ancestors)
|
if (!tinfo->simple_ancestors)
|
||||||
traverse_offset_bases(inst->value, tinfo, self, deregister_instance_impl);
|
traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new instance which, by default, includes allocation (but not construction of) the
|
/// Instance creation function for all pybind11 types. It only allocates space for the C++ object
|
||||||
/// wrapped C++ instance. If allocating value, the instance is registered; otherwise
|
/// (or multiple objects, for Python-side inheritance from multiple pybind11 types), but doesn't
|
||||||
/// register_instance will need to be called once the value has been assigned.
|
/// call the constructor -- an `__init__` function must do that. If allocating value, the instance
|
||||||
|
/// is registered; otherwise register_instance will need to be called once the value has been
|
||||||
|
/// assigned.
|
||||||
inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= true (in cast.h)*/) {
|
inline PyObject *make_new_instance(PyTypeObject *type, bool allocate_value /*= true (in cast.h)*/) {
|
||||||
PyObject *self = type->tp_alloc(type, 0);
|
#if defined(PYPY_VERSION)
|
||||||
auto instance = (instance_essentials<void> *) self;
|
// PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited
|
||||||
auto tinfo = get_type_info(type);
|
// object is a a plain Python type (i.e. not derived from an extension type). Fix it.
|
||||||
instance->owned = true;
|
ssize_t instance_size = static_cast<ssize_t>(sizeof(instance));
|
||||||
instance->holder_constructed = false;
|
if (type->tp_basicsize < instance_size) {
|
||||||
if (allocate_value) {
|
type->tp_basicsize = instance_size;
|
||||||
instance->value = tinfo->operator_new(tinfo->type_size);
|
|
||||||
register_instance(self, tinfo);
|
|
||||||
} else {
|
|
||||||
instance->value = nullptr;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
PyObject *self = type->tp_alloc(type, 0);
|
||||||
|
auto inst = reinterpret_cast<instance *>(self);
|
||||||
|
// Allocate the value/holder internals:
|
||||||
|
inst->allocate_layout();
|
||||||
|
|
||||||
|
inst->owned = true;
|
||||||
|
// Allocate (if requested) the value pointers; otherwise leave them as nullptr
|
||||||
|
if (allocate_value) {
|
||||||
|
for (auto &v_h : values_and_holders(inst)) {
|
||||||
|
void *&vptr = v_h.value_ptr();
|
||||||
|
vptr = v_h.type->operator_new(v_h.type->type_size);
|
||||||
|
register_instance(inst, vptr, v_h.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,18 +283,20 @@ extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject
|
|||||||
/// Clears all internal data from the instance and removes it from registered instances in
|
/// Clears all internal data from the instance and removes it from registered instances in
|
||||||
/// preparation for deallocation.
|
/// preparation for deallocation.
|
||||||
inline void clear_instance(PyObject *self) {
|
inline void clear_instance(PyObject *self) {
|
||||||
auto instance = (instance_essentials<void> *) self;
|
auto instance = reinterpret_cast<detail::instance *>(self);
|
||||||
bool has_value = instance->value;
|
|
||||||
type_info *tinfo = nullptr;
|
// Deallocate any values/holders, if present:
|
||||||
if (has_value || instance->holder_constructed) {
|
for (auto &v_h : values_and_holders(instance)) {
|
||||||
auto type = Py_TYPE(self);
|
if (v_h) {
|
||||||
tinfo = get_type_info(type);
|
if (instance->owned || v_h.holder_constructed())
|
||||||
tinfo->dealloc(self);
|
v_h.type->dealloc(v_h);
|
||||||
}
|
|
||||||
if (has_value) {
|
if (!deregister_instance(instance, v_h.value_ptr(), v_h.type))
|
||||||
if (!tinfo) tinfo = get_type_info(Py_TYPE(self));
|
|
||||||
if (!deregister_instance(self, tinfo))
|
|
||||||
pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!");
|
pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Deallocate the value/holder layout internals:
|
||||||
|
instance->deallocate_layout();
|
||||||
|
|
||||||
if (instance->weakrefs)
|
if (instance->weakrefs)
|
||||||
PyObject_ClearWeakRefs(self);
|
PyObject_ClearWeakRefs(self);
|
||||||
@ -320,7 +305,6 @@ inline void clear_instance(PyObject *self) {
|
|||||||
if (dict_ptr)
|
if (dict_ptr)
|
||||||
Py_CLEAR(*dict_ptr);
|
Py_CLEAR(*dict_ptr);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
|
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
|
||||||
/// to destroy the C++ object itself, while the rest is Python bookkeeping.
|
/// to destroy the C++ object itself, while the rest is Python bookkeeping.
|
||||||
@ -329,19 +313,17 @@ extern "C" inline void pybind11_object_dealloc(PyObject *self) {
|
|||||||
Py_TYPE(self)->tp_free(self);
|
Py_TYPE(self)->tp_free(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create a type which can be used as a common base for all classes with the same
|
/** Create the type which can be used as a common base for all classes. This is
|
||||||
instance size, i.e. all classes with the same `sizeof(holder_type)`. This is
|
|
||||||
needed in order to satisfy Python's requirements for multiple inheritance.
|
needed in order to satisfy Python's requirements for multiple inheritance.
|
||||||
Return value: New reference. */
|
Return value: New reference. */
|
||||||
inline PyObject *make_object_base_type(size_t instance_size) {
|
inline PyObject *make_object_base_type(PyTypeObject *metaclass) {
|
||||||
auto name = "pybind11_object_" + std::to_string(instance_size);
|
constexpr auto *name = "pybind11_object";
|
||||||
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name.c_str()));
|
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name));
|
||||||
|
|
||||||
/* Danger zone: from now (and until PyType_Ready), make sure to
|
/* Danger zone: from now (and until PyType_Ready), make sure to
|
||||||
issue no Python C API calls which could potentially invoke the
|
issue no Python C API calls which could potentially invoke the
|
||||||
garbage collector (the GC will call type_traverse(), which will in
|
garbage collector (the GC will call type_traverse(), which will in
|
||||||
turn find the newly constructed type in an invalid state) */
|
turn find the newly constructed type in an invalid state) */
|
||||||
auto metaclass = get_internals().default_metaclass;
|
|
||||||
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
|
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
|
||||||
if (!heap_type)
|
if (!heap_type)
|
||||||
pybind11_fail("make_object_base_type(): error allocating type!");
|
pybind11_fail("make_object_base_type(): error allocating type!");
|
||||||
@ -352,9 +334,9 @@ inline PyObject *make_object_base_type(size_t instance_size) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto type = &heap_type->ht_type;
|
auto type = &heap_type->ht_type;
|
||||||
type->tp_name = strdup(name.c_str());
|
type->tp_name = name;
|
||||||
type->tp_base = &PyBaseObject_Type;
|
type->tp_base = &PyBaseObject_Type;
|
||||||
type->tp_basicsize = static_cast<ssize_t>(instance_size);
|
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
|
||||||
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
||||||
|
|
||||||
type->tp_new = pybind11_object_new;
|
type->tp_new = pybind11_object_new;
|
||||||
@ -362,7 +344,7 @@ inline PyObject *make_object_base_type(size_t instance_size) {
|
|||||||
type->tp_dealloc = pybind11_object_dealloc;
|
type->tp_dealloc = pybind11_object_dealloc;
|
||||||
|
|
||||||
/* Support weak references (needed for the keep_alive feature) */
|
/* Support weak references (needed for the keep_alive feature) */
|
||||||
type->tp_weaklistoffset = offsetof(instance_essentials<void>, weakrefs);
|
type->tp_weaklistoffset = offsetof(instance, weakrefs);
|
||||||
|
|
||||||
if (PyType_Ready(type) < 0)
|
if (PyType_Ready(type) < 0)
|
||||||
pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string());
|
pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string());
|
||||||
@ -373,20 +355,6 @@ inline PyObject *make_object_base_type(size_t instance_size) {
|
|||||||
return (PyObject *) heap_type;
|
return (PyObject *) heap_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the appropriate base type for the given instance size. The results are cached
|
|
||||||
in `internals.bases` so that only a single base is ever created for any size value.
|
|
||||||
Return value: Borrowed reference. */
|
|
||||||
inline PyObject *internals::get_base(size_t instance_size) {
|
|
||||||
auto it = bases.find(instance_size);
|
|
||||||
if (it != bases.end()) {
|
|
||||||
return it->second;
|
|
||||||
} else {
|
|
||||||
auto base = make_object_base_type(instance_size);
|
|
||||||
bases[instance_size] = base;
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// dynamic_attr: Support for `d = instance.__dict__`.
|
/// dynamic_attr: Support for `d = instance.__dict__`.
|
||||||
extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
|
extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
|
||||||
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
PyObject *&dict = *_PyObject_GetDictPtr(self);
|
||||||
@ -460,7 +428,7 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
|
|||||||
PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
|
PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memset(view, 0, sizeof(Py_buffer));
|
std::memset(view, 0, sizeof(Py_buffer));
|
||||||
buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data);
|
buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data);
|
||||||
view->obj = obj;
|
view->obj = obj;
|
||||||
view->ndim = 1;
|
view->ndim = 1;
|
||||||
@ -536,7 +504,7 @@ inline PyObject* make_new_python_type(const type_record &rec) {
|
|||||||
|
|
||||||
auto &internals = get_internals();
|
auto &internals = get_internals();
|
||||||
auto bases = tuple(rec.bases);
|
auto bases = tuple(rec.bases);
|
||||||
auto base = (bases.size() == 0) ? internals.get_base(rec.instance_size)
|
auto base = (bases.size() == 0) ? internals.instance_base
|
||||||
: bases[0].ptr();
|
: bases[0].ptr();
|
||||||
|
|
||||||
/* Danger zone: from now (and until PyType_Ready), make sure to
|
/* Danger zone: from now (and until PyType_Ready), make sure to
|
||||||
@ -559,7 +527,7 @@ inline PyObject* make_new_python_type(const type_record &rec) {
|
|||||||
type->tp_name = strdup(full_name.c_str());
|
type->tp_name = strdup(full_name.c_str());
|
||||||
type->tp_doc = tp_doc;
|
type->tp_doc = tp_doc;
|
||||||
type->tp_base = (PyTypeObject *) handle(base).inc_ref().ptr();
|
type->tp_base = (PyTypeObject *) handle(base).inc_ref().ptr();
|
||||||
type->tp_basicsize = static_cast<ssize_t>(rec.instance_size);
|
type->tp_basicsize = static_cast<ssize_t>(sizeof(instance));
|
||||||
if (bases.size() > 0)
|
if (bases.size() > 0)
|
||||||
type->tp_bases = bases.release().ptr();
|
type->tp_bases = bases.release().ptr();
|
||||||
|
|
||||||
|
@ -346,21 +346,77 @@ NAMESPACE_BEGIN(detail)
|
|||||||
|
|
||||||
inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); }
|
inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); }
|
||||||
|
|
||||||
|
// Returns the size as a multiple of sizeof(void *), rounded up.
|
||||||
|
inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); }
|
||||||
|
|
||||||
inline std::string error_string();
|
inline std::string error_string();
|
||||||
|
|
||||||
/// Core part of the 'instance' type which POD (needed to be able to use 'offsetof')
|
/**
|
||||||
template <typename type> struct instance_essentials {
|
* The space to allocate for simple layout instance holders (see below) in multiple of the size of
|
||||||
|
* a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required
|
||||||
|
* to holder either a std::unique_ptr or std::shared_ptr (which is almost always
|
||||||
|
* sizeof(std::shared_ptr<T>)).
|
||||||
|
*/
|
||||||
|
constexpr size_t instance_simple_holder_in_ptrs() {
|
||||||
|
static_assert(sizeof(std::shared_ptr<int>) >= sizeof(std::unique_ptr<int>),
|
||||||
|
"pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs");
|
||||||
|
return size_in_ptrs(sizeof(std::shared_ptr<int>));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
struct type_info;
|
||||||
|
struct value_and_holder;
|
||||||
|
|
||||||
|
/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof')
|
||||||
|
struct instance {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
type *value;
|
/// Storage for pointers and holder; see simple_layout, below, for a description
|
||||||
|
union {
|
||||||
|
void *simple_value_holder[1 + instance_simple_holder_in_ptrs()];
|
||||||
|
struct {
|
||||||
|
void **values_and_holders;
|
||||||
|
bool *holder_constructed;
|
||||||
|
} nonsimple;
|
||||||
|
};
|
||||||
|
/// Weak references (needed for keep alive):
|
||||||
PyObject *weakrefs;
|
PyObject *weakrefs;
|
||||||
|
/// If true, the pointer is owned which means we're free to manage it with a holder.
|
||||||
bool owned : 1;
|
bool owned : 1;
|
||||||
bool holder_constructed : 1;
|
/**
|
||||||
|
* An instance has two possible value/holder layouts.
|
||||||
|
*
|
||||||
|
* Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer
|
||||||
|
* and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied
|
||||||
|
* whenever there is no python-side multiple inheritance of bound C++ types *and* the type's
|
||||||
|
* holder will fit in the default space (which is large enough to hold either a std::unique_ptr
|
||||||
|
* or std::shared_ptr).
|
||||||
|
*
|
||||||
|
* Non-simple layout applies when using custom holders that require more space than `shared_ptr`
|
||||||
|
* (which is typically the size of two pointers), or when multiple inheritance is used on the
|
||||||
|
* python side. Non-simple layout allocates the required amount of memory to have multiple
|
||||||
|
* bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a
|
||||||
|
* pointer to allocated space of the required space to hold a a sequence of value pointers and
|
||||||
|
* holders followed by a set of holder-constructed flags (1 byte each), i.e.
|
||||||
|
* [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of
|
||||||
|
* `sizeof(void *)`. `nonsimple.holder_constructed` is, for convenience, a pointer to the
|
||||||
|
* beginning of the [bb...] block (but not independently allocated).
|
||||||
|
*/
|
||||||
|
bool simple_layout : 1;
|
||||||
|
/// For simple layout, tracks whether the holder has been constructed
|
||||||
|
bool simple_holder_constructed : 1;
|
||||||
|
|
||||||
|
/// Initializes all of the above type/values/holders data
|
||||||
|
void allocate_layout();
|
||||||
|
|
||||||
|
/// Destroys/deallocates all of the above
|
||||||
|
void deallocate_layout();
|
||||||
|
|
||||||
|
/// Returns the value_and_holder wrapper for the given type (or the first, if `find_type`
|
||||||
|
/// omitted)
|
||||||
|
value_and_holder get_value_and_holder(const type_info *find_type = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// PyObject wrapper around generic types, includes a special holder type that is responsible for lifetime management
|
static_assert(std::is_standard_layout<instance>::value, "Internal error: `pybind11::detail::instance` is not standard layout!");
|
||||||
template <typename type, typename holder_type = std::unique_ptr<type>> struct instance : instance_essentials<type> {
|
|
||||||
holder_type holder;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct overload_hash {
|
struct overload_hash {
|
||||||
inline size_t operator()(const std::pair<const PyObject *, const char *>& v) const {
|
inline size_t operator()(const std::pair<const PyObject *, const char *>& v) const {
|
||||||
@ -373,22 +429,19 @@ struct overload_hash {
|
|||||||
/// 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
|
std::unordered_map<std::type_index, void*> registered_types_cpp; // std::type_index -> type_info
|
||||||
std::unordered_map<const void *, void*> registered_types_py; // PyTypeObject* -> type_info
|
std::unordered_map<PyTypeObject *, std::vector<type_info *>> registered_types_py; // PyTypeObject* -> base type_info(s)
|
||||||
std::unordered_multimap<const void *, void*> registered_instances; // void * -> PyObject*
|
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;
|
std::unordered_map<std::type_index, 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;
|
||||||
PyTypeObject *default_metaclass;
|
PyTypeObject *default_metaclass;
|
||||||
std::unordered_map<size_t, PyObject *> bases; // one base type per `instance_size` (very few)
|
PyObject *instance_base;
|
||||||
#if defined(WITH_THREAD)
|
#if defined(WITH_THREAD)
|
||||||
decltype(PyThread_create_key()) tstate = 0; // Usually an int but a long on Cygwin64 with Python 3.x
|
decltype(PyThread_create_key()) tstate = 0; // Usually an int but a long on Cygwin64 with Python 3.x
|
||||||
PyInterpreterState *istate = nullptr;
|
PyInterpreterState *istate = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Return the appropriate base type for the given instance size
|
|
||||||
PyObject *get_base(size_t instance_size);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Return a reference to the current 'internals' information
|
/// Return a reference to the current 'internals' information
|
||||||
|
@ -293,7 +293,7 @@ protected:
|
|||||||
if (!chain) {
|
if (!chain) {
|
||||||
/* No existing overload was found, create a new function object */
|
/* No existing overload was found, create a new function object */
|
||||||
rec->def = new PyMethodDef();
|
rec->def = new PyMethodDef();
|
||||||
memset(rec->def, 0, sizeof(PyMethodDef));
|
std::memset(rec->def, 0, sizeof(PyMethodDef));
|
||||||
rec->def->ml_name = rec->name;
|
rec->def->ml_name = rec->name;
|
||||||
rec->def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher);
|
rec->def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher);
|
||||||
rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
|
rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
|
||||||
@ -707,10 +707,8 @@ protected:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
} else {
|
} else {
|
||||||
if (overloads->is_constructor) {
|
if (overloads->is_constructor) {
|
||||||
/* When a constructor ran successfully, the corresponding
|
auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr());
|
||||||
holder type (e.g. std::unique_ptr) must still be initialized. */
|
tinfo->init_holder(reinterpret_cast<instance *>(parent.ptr()), nullptr);
|
||||||
auto tinfo = get_type_info(Py_TYPE(parent.ptr()));
|
|
||||||
tinfo->init_holder(parent.ptr(), nullptr);
|
|
||||||
}
|
}
|
||||||
return result.ptr();
|
return result.ptr();
|
||||||
}
|
}
|
||||||
@ -727,7 +725,7 @@ public:
|
|||||||
if (!options::show_user_defined_docstrings()) doc = nullptr;
|
if (!options::show_user_defined_docstrings()) doc = nullptr;
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
PyModuleDef *def = new PyModuleDef();
|
PyModuleDef *def = new PyModuleDef();
|
||||||
memset(def, 0, sizeof(PyModuleDef));
|
std::memset(def, 0, sizeof(PyModuleDef));
|
||||||
def->m_name = name;
|
def->m_name = name;
|
||||||
def->m_doc = doc;
|
def->m_doc = doc;
|
||||||
def->m_size = -1;
|
def->m_size = -1;
|
||||||
@ -830,6 +828,7 @@ protected:
|
|||||||
tinfo->cpptype = rec.type;
|
tinfo->cpptype = rec.type;
|
||||||
tinfo->type_size = rec.type_size;
|
tinfo->type_size = rec.type_size;
|
||||||
tinfo->operator_new = rec.operator_new;
|
tinfo->operator_new = rec.operator_new;
|
||||||
|
tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size);
|
||||||
tinfo->init_holder = rec.init_holder;
|
tinfo->init_holder = rec.init_holder;
|
||||||
tinfo->dealloc = rec.dealloc;
|
tinfo->dealloc = rec.dealloc;
|
||||||
tinfo->simple_type = true;
|
tinfo->simple_type = true;
|
||||||
@ -840,7 +839,7 @@ protected:
|
|||||||
tinfo->direct_conversions = &internals.direct_conversions[tindex];
|
tinfo->direct_conversions = &internals.direct_conversions[tindex];
|
||||||
tinfo->default_holder = rec.default_holder;
|
tinfo->default_holder = rec.default_holder;
|
||||||
internals.registered_types_cpp[tindex] = tinfo;
|
internals.registered_types_cpp[tindex] = tinfo;
|
||||||
internals.registered_types_py[m_ptr] = tinfo;
|
internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo };
|
||||||
|
|
||||||
if (rec.bases.size() > 1 || rec.multiple_inheritance) {
|
if (rec.bases.size() > 1 || rec.multiple_inheritance) {
|
||||||
mark_parents_nonsimple(tinfo->type);
|
mark_parents_nonsimple(tinfo->type);
|
||||||
@ -923,7 +922,6 @@ public:
|
|||||||
using type_alias = detail::exactly_one_t<is_subtype, void, options...>;
|
using type_alias = detail::exactly_one_t<is_subtype, void, options...>;
|
||||||
constexpr static bool has_alias = !std::is_void<type_alias>::value;
|
constexpr static bool has_alias = !std::is_void<type_alias>::value;
|
||||||
using holder_type = detail::exactly_one_t<is_holder, std::unique_ptr<type>, options...>;
|
using holder_type = detail::exactly_one_t<is_holder, std::unique_ptr<type>, options...>;
|
||||||
using instance_type = detail::instance<type, holder_type>;
|
|
||||||
|
|
||||||
static_assert(detail::all_of<is_valid_class_option<options>...>::value,
|
static_assert(detail::all_of<is_valid_class_option<options>...>::value,
|
||||||
"Unknown/invalid class_ template parameters provided");
|
"Unknown/invalid class_ template parameters provided");
|
||||||
@ -947,7 +945,7 @@ public:
|
|||||||
record.name = name;
|
record.name = name;
|
||||||
record.type = &typeid(type);
|
record.type = &typeid(type);
|
||||||
record.type_size = sizeof(conditional_t<has_alias, type_alias, type>);
|
record.type_size = sizeof(conditional_t<has_alias, type_alias, type>);
|
||||||
record.instance_size = sizeof(instance_type);
|
record.holder_size = sizeof(holder_type);
|
||||||
record.init_holder = init_holder;
|
record.init_holder = init_holder;
|
||||||
record.dealloc = dealloc;
|
record.dealloc = dealloc;
|
||||||
record.default_holder = std::is_same<holder_type, std::unique_ptr<type>>::value;
|
record.default_holder = std::is_same<holder_type, std::unique_ptr<type>>::value;
|
||||||
@ -1139,53 +1137,57 @@ public:
|
|||||||
private:
|
private:
|
||||||
/// Initialize holder object, variant 1: object derives from enable_shared_from_this
|
/// Initialize holder object, variant 1: object derives from enable_shared_from_this
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static void init_holder_helper(instance_type *inst, const holder_type * /* unused */, const std::enable_shared_from_this<T> * /* dummy */) {
|
static void init_holder_helper(detail::instance *inst, detail::value_and_holder &v_h,
|
||||||
|
const holder_type * /* unused */, const std::enable_shared_from_this<T> * /* dummy */) {
|
||||||
try {
|
try {
|
||||||
auto sh = std::dynamic_pointer_cast<typename holder_type::element_type>(inst->value->shared_from_this());
|
auto sh = std::dynamic_pointer_cast<typename holder_type::element_type>(
|
||||||
|
v_h.value_ptr<type>()->shared_from_this());
|
||||||
if (sh) {
|
if (sh) {
|
||||||
new (&inst->holder) holder_type(std::move(sh));
|
new (&v_h.holder<holder_type>()) holder_type(std::move(sh));
|
||||||
inst->holder_constructed = true;
|
v_h.set_holder_constructed();
|
||||||
}
|
}
|
||||||
} catch (const std::bad_weak_ptr &) {}
|
} catch (const std::bad_weak_ptr &) {}
|
||||||
if (!inst->holder_constructed && inst->owned) {
|
|
||||||
new (&inst->holder) holder_type(inst->value);
|
if (!v_h.holder_constructed() && inst->owned) {
|
||||||
inst->holder_constructed = true;
|
new (&v_h.holder<holder_type>()) holder_type(v_h.value_ptr<type>());
|
||||||
|
v_h.set_holder_constructed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_holder_from_existing(instance_type *inst, const holder_type *holder_ptr,
|
static void init_holder_from_existing(const detail::value_and_holder &v_h,
|
||||||
std::true_type /*is_copy_constructible*/) {
|
const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) {
|
||||||
new (&inst->holder) holder_type(*holder_ptr);
|
new (&v_h.holder<holder_type>()) holder_type(*reinterpret_cast<const holder_type *>(holder_ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_holder_from_existing(instance_type *inst, const holder_type *holder_ptr,
|
static void init_holder_from_existing(const detail::value_and_holder &v_h,
|
||||||
std::false_type /*is_copy_constructible*/) {
|
const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) {
|
||||||
new (&inst->holder) holder_type(std::move(*const_cast<holder_type *>(holder_ptr)));
|
new (&v_h.holder<holder_type>()) holder_type(std::move(*const_cast<holder_type *>(holder_ptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize holder object, variant 2: try to construct from existing holder object, if possible
|
/// Initialize holder object, variant 2: try to construct from existing holder object, if possible
|
||||||
static void init_holder_helper(instance_type *inst, const holder_type *holder_ptr, const void * /* dummy */) {
|
static void init_holder_helper(detail::instance *inst, detail::value_and_holder &v_h,
|
||||||
|
const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this<T>) */) {
|
||||||
if (holder_ptr) {
|
if (holder_ptr) {
|
||||||
init_holder_from_existing(inst, holder_ptr, std::is_copy_constructible<holder_type>());
|
init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible<holder_type>());
|
||||||
inst->holder_constructed = true;
|
v_h.set_holder_constructed();
|
||||||
} else if (inst->owned || detail::always_construct_holder<holder_type>::value) {
|
} else if (inst->owned || detail::always_construct_holder<holder_type>::value) {
|
||||||
new (&inst->holder) holder_type(inst->value);
|
new (&v_h.holder<holder_type>()) holder_type(v_h.value_ptr<type>());
|
||||||
inst->holder_constructed = true;
|
v_h.set_holder_constructed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize holder object of an instance, possibly given a pointer to an existing holder
|
/// Initialize holder object of an instance, possibly given a pointer to an existing holder
|
||||||
static void init_holder(PyObject *inst_, const void *holder_ptr) {
|
static void init_holder(detail::instance *inst, const void *holder_ptr) {
|
||||||
auto inst = (instance_type *) inst_;
|
auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type)));
|
||||||
init_holder_helper(inst, (const holder_type *) holder_ptr, inst->value);
|
init_holder_helper(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr<type>());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dealloc(PyObject *inst_) {
|
/// Deallocates an instance; via holder, if constructed; otherwise via operator delete.
|
||||||
instance_type *inst = (instance_type *) inst_;
|
static void dealloc(const detail::value_and_holder &v_h) {
|
||||||
if (inst->holder_constructed)
|
if (v_h.holder_constructed())
|
||||||
inst->holder.~holder_type();
|
v_h.holder<holder_type>().~holder_type();
|
||||||
else if (inst->owned)
|
else
|
||||||
detail::call_operator_delete(inst->value);
|
detail::call_operator_delete(v_h.value_ptr<type>());
|
||||||
}
|
}
|
||||||
|
|
||||||
static detail::function_record *get_function_record(handle h) {
|
static detail::function_record *get_function_record(handle h) {
|
||||||
@ -1349,6 +1351,25 @@ PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, func
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::pair<decltype(internals::registered_types_py)::iterator, bool> all_type_info_get_cache(PyTypeObject *type) {
|
||||||
|
auto res = get_internals().registered_types_py
|
||||||
|
#ifdef z__cpp_lib_unordered_map_try_emplace
|
||||||
|
.try_emplace(type);
|
||||||
|
#else
|
||||||
|
.emplace(type, std::vector<detail::type_info *>());
|
||||||
|
#endif
|
||||||
|
if (res.second) {
|
||||||
|
// New cache entry created; set up a weak reference to automatically remove it if the type
|
||||||
|
// gets destroyed:
|
||||||
|
weakref((PyObject *) type, cpp_function([type](handle wr) {
|
||||||
|
get_internals().registered_types_py.erase(type);
|
||||||
|
wr.dec_ref();
|
||||||
|
})).release();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Iterator, typename Sentinel, bool KeyIterator, return_value_policy Policy>
|
template <typename Iterator, typename Sentinel, bool KeyIterator, return_value_policy Policy>
|
||||||
struct iterator_state {
|
struct iterator_state {
|
||||||
Iterator it;
|
Iterator it;
|
||||||
|
@ -169,7 +169,7 @@ public:
|
|||||||
auto &internals = py::detail::get_internals();
|
auto &internals = py::detail::get_internals();
|
||||||
const std::type_index *t1 = nullptr, *t2 = nullptr;
|
const std::type_index *t1 = nullptr, *t2 = nullptr;
|
||||||
try {
|
try {
|
||||||
auto *type_info = internals.registered_types_py.at(class_.ptr());
|
auto *type_info = internals.registered_types_py.at((PyTypeObject *) class_.ptr()).at(0);
|
||||||
for (auto &p : internals.registered_types_cpp) {
|
for (auto &p : internals.registered_types_cpp) {
|
||||||
if (p.second == type_info) {
|
if (p.second == type_info) {
|
||||||
if (t1) {
|
if (t1) {
|
||||||
|
@ -23,6 +23,11 @@ struct Base2 {
|
|||||||
int i;
|
int i;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <int N> struct BaseN {
|
||||||
|
BaseN(int i) : i(i) { }
|
||||||
|
int i;
|
||||||
|
};
|
||||||
|
|
||||||
struct Base12 : Base1, Base2 {
|
struct Base12 : Base1, Base2 {
|
||||||
Base12(int i, int j) : Base1(i), Base2(j) { }
|
Base12(int i, int j) : Base1(i), Base2(j) { }
|
||||||
};
|
};
|
||||||
@ -45,6 +50,15 @@ test_initializer multiple_inheritance([](py::module &m) {
|
|||||||
py::class_<MIType, Base12>(m, "MIType")
|
py::class_<MIType, Base12>(m, "MIType")
|
||||||
.def(py::init<int, int>());
|
.def(py::init<int, int>());
|
||||||
|
|
||||||
|
// Many bases for testing that multiple inheritance from many classes (i.e. requiring extra
|
||||||
|
// space for holder constructed flags) works.
|
||||||
|
#define PYBIND11_BASEN(N) py::class_<BaseN<N>>(m, "BaseN" #N).def(py::init<int>()).def("f" #N, [](BaseN<N> &b) { return b.i + N; })
|
||||||
|
PYBIND11_BASEN( 1); PYBIND11_BASEN( 2); PYBIND11_BASEN( 3); PYBIND11_BASEN( 4);
|
||||||
|
PYBIND11_BASEN( 5); PYBIND11_BASEN( 6); PYBIND11_BASEN( 7); PYBIND11_BASEN( 8);
|
||||||
|
PYBIND11_BASEN( 9); PYBIND11_BASEN(10); PYBIND11_BASEN(11); PYBIND11_BASEN(12);
|
||||||
|
PYBIND11_BASEN(13); PYBIND11_BASEN(14); PYBIND11_BASEN(15); PYBIND11_BASEN(16);
|
||||||
|
PYBIND11_BASEN(17);
|
||||||
|
|
||||||
// Uncommenting this should result in a compile time failure (MI can only be specified via
|
// Uncommenting this should result in a compile time failure (MI can only be specified via
|
||||||
// template parameters because pybind has to know the types involved; see discussion in #742 for
|
// template parameters because pybind has to know the types involved; see discussion in #742 for
|
||||||
// details).
|
// details).
|
||||||
|
@ -53,15 +53,174 @@ def test_multiple_inheritance_mix2():
|
|||||||
assert mt.bar() == 4
|
assert mt.bar() == 4
|
||||||
|
|
||||||
|
|
||||||
def test_multiple_inheritance_error():
|
def test_multiple_inheritance_python():
|
||||||
"""Inheriting from multiple C++ bases in Python is not supported"""
|
|
||||||
from pybind11_tests import Base1, Base2
|
from pybind11_tests import Base1, Base2
|
||||||
|
|
||||||
with pytest.raises(TypeError) as excinfo:
|
class MI1(Base1, Base2):
|
||||||
# noinspection PyUnusedLocal
|
def __init__(self, i, j):
|
||||||
class MI(Base1, Base2):
|
Base1.__init__(self, i)
|
||||||
pass
|
Base2.__init__(self, j)
|
||||||
assert "Can't inherit from multiple C++ classes in Python" in str(excinfo.value)
|
|
||||||
|
class B1(object):
|
||||||
|
def v(self):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
class MI2(B1, Base1, Base2):
|
||||||
|
def __init__(self, i, j):
|
||||||
|
B1.__init__(self)
|
||||||
|
Base1.__init__(self, i)
|
||||||
|
Base2.__init__(self, j)
|
||||||
|
|
||||||
|
class MI3(MI2):
|
||||||
|
def __init__(self, i, j):
|
||||||
|
MI2.__init__(self, i, j)
|
||||||
|
|
||||||
|
class MI4(MI3, Base2):
|
||||||
|
def __init__(self, i, j, k):
|
||||||
|
MI2.__init__(self, j, k)
|
||||||
|
Base2.__init__(self, i)
|
||||||
|
|
||||||
|
class MI5(Base2, B1, Base1):
|
||||||
|
def __init__(self, i, j):
|
||||||
|
B1.__init__(self)
|
||||||
|
Base1.__init__(self, i)
|
||||||
|
Base2.__init__(self, j)
|
||||||
|
|
||||||
|
class MI6(Base2, B1):
|
||||||
|
def __init__(self, i):
|
||||||
|
Base2.__init__(self, i)
|
||||||
|
B1.__init__(self)
|
||||||
|
|
||||||
|
class B2(B1):
|
||||||
|
def v(self):
|
||||||
|
return 2
|
||||||
|
|
||||||
|
class B3(object):
|
||||||
|
def v(self):
|
||||||
|
return 3
|
||||||
|
|
||||||
|
class B4(B3, B2):
|
||||||
|
def v(self):
|
||||||
|
return 4
|
||||||
|
|
||||||
|
class MI7(B4, MI6):
|
||||||
|
def __init__(self, i):
|
||||||
|
B4.__init__(self)
|
||||||
|
MI6.__init__(self, i)
|
||||||
|
|
||||||
|
class MI8(MI6, B3):
|
||||||
|
def __init__(self, i):
|
||||||
|
MI6.__init__(self, i)
|
||||||
|
B3.__init__(self)
|
||||||
|
|
||||||
|
class MI8b(B3, MI6):
|
||||||
|
def __init__(self, i):
|
||||||
|
B3.__init__(self)
|
||||||
|
MI6.__init__(self, i)
|
||||||
|
|
||||||
|
mi1 = MI1(1, 2)
|
||||||
|
assert mi1.foo() == 1
|
||||||
|
assert mi1.bar() == 2
|
||||||
|
|
||||||
|
mi2 = MI2(3, 4)
|
||||||
|
assert mi2.v() == 1
|
||||||
|
assert mi2.foo() == 3
|
||||||
|
assert mi2.bar() == 4
|
||||||
|
|
||||||
|
mi3 = MI3(5, 6)
|
||||||
|
assert mi3.v() == 1
|
||||||
|
assert mi3.foo() == 5
|
||||||
|
assert mi3.bar() == 6
|
||||||
|
|
||||||
|
mi4 = MI4(7, 8, 9)
|
||||||
|
assert mi4.v() == 1
|
||||||
|
assert mi4.foo() == 8
|
||||||
|
assert mi4.bar() == 7
|
||||||
|
|
||||||
|
mi5 = MI5(10, 11)
|
||||||
|
assert mi5.v() == 1
|
||||||
|
assert mi5.foo() == 10
|
||||||
|
assert mi5.bar() == 11
|
||||||
|
|
||||||
|
mi6 = MI6(12)
|
||||||
|
assert mi6.v() == 1
|
||||||
|
assert mi6.bar() == 12
|
||||||
|
|
||||||
|
mi7 = MI7(13)
|
||||||
|
assert mi7.v() == 4
|
||||||
|
assert mi7.bar() == 13
|
||||||
|
|
||||||
|
mi8 = MI8(14)
|
||||||
|
assert mi8.v() == 1
|
||||||
|
assert mi8.bar() == 14
|
||||||
|
|
||||||
|
mi8b = MI8b(15)
|
||||||
|
assert mi8b.v() == 3
|
||||||
|
assert mi8b.bar() == 15
|
||||||
|
|
||||||
|
|
||||||
|
def test_multiple_inheritance_python_many_bases():
|
||||||
|
from pybind11_tests import (BaseN1, BaseN2, BaseN3, BaseN4, BaseN5, BaseN6, BaseN7,
|
||||||
|
BaseN8, BaseN9, BaseN10, BaseN11, BaseN12, BaseN13, BaseN14,
|
||||||
|
BaseN15, BaseN16, BaseN17)
|
||||||
|
|
||||||
|
class MIMany14(BaseN1, BaseN2, BaseN3, BaseN4):
|
||||||
|
def __init__(self):
|
||||||
|
BaseN1.__init__(self, 1)
|
||||||
|
BaseN2.__init__(self, 2)
|
||||||
|
BaseN3.__init__(self, 3)
|
||||||
|
BaseN4.__init__(self, 4)
|
||||||
|
|
||||||
|
class MIMany58(BaseN5, BaseN6, BaseN7, BaseN8):
|
||||||
|
def __init__(self):
|
||||||
|
BaseN5.__init__(self, 5)
|
||||||
|
BaseN6.__init__(self, 6)
|
||||||
|
BaseN7.__init__(self, 7)
|
||||||
|
BaseN8.__init__(self, 8)
|
||||||
|
|
||||||
|
class MIMany916(BaseN9, BaseN10, BaseN11, BaseN12, BaseN13, BaseN14, BaseN15, BaseN16):
|
||||||
|
def __init__(self):
|
||||||
|
BaseN9.__init__(self, 9)
|
||||||
|
BaseN10.__init__(self, 10)
|
||||||
|
BaseN11.__init__(self, 11)
|
||||||
|
BaseN12.__init__(self, 12)
|
||||||
|
BaseN13.__init__(self, 13)
|
||||||
|
BaseN14.__init__(self, 14)
|
||||||
|
BaseN15.__init__(self, 15)
|
||||||
|
BaseN16.__init__(self, 16)
|
||||||
|
|
||||||
|
class MIMany19(MIMany14, MIMany58, BaseN9):
|
||||||
|
def __init__(self):
|
||||||
|
MIMany14.__init__(self)
|
||||||
|
MIMany58.__init__(self)
|
||||||
|
BaseN9.__init__(self, 9)
|
||||||
|
|
||||||
|
class MIMany117(MIMany14, MIMany58, MIMany916, BaseN17):
|
||||||
|
def __init__(self):
|
||||||
|
MIMany14.__init__(self)
|
||||||
|
MIMany58.__init__(self)
|
||||||
|
MIMany916.__init__(self)
|
||||||
|
BaseN17.__init__(self, 17)
|
||||||
|
|
||||||
|
# Inherits from 4 registered C++ classes: can fit in one pointer on any modern arch:
|
||||||
|
a = MIMany14()
|
||||||
|
for i in range(1, 4):
|
||||||
|
assert getattr(a, "f" + str(i))() == 2 * i
|
||||||
|
|
||||||
|
# Inherits from 8: requires 1/2 pointers worth of holder flags on 32/64-bit arch:
|
||||||
|
b = MIMany916()
|
||||||
|
for i in range(9, 16):
|
||||||
|
assert getattr(b, "f" + str(i))() == 2 * i
|
||||||
|
|
||||||
|
# Inherits from 9: requires >= 2 pointers worth of holder flags
|
||||||
|
c = MIMany19()
|
||||||
|
for i in range(1, 9):
|
||||||
|
assert getattr(c, "f" + str(i))() == 2 * i
|
||||||
|
|
||||||
|
# Inherits from 17: requires >= 3 pointers worth of holder flags
|
||||||
|
d = MIMany117()
|
||||||
|
for i in range(1, 17):
|
||||||
|
assert getattr(d, "f" + str(i))() == 2 * i
|
||||||
|
|
||||||
|
|
||||||
def test_multiple_inheritance_virtbase():
|
def test_multiple_inheritance_virtbase():
|
||||||
|
@ -81,6 +81,28 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the
|
||||||
|
/// holder size to trigger the non-simple-layout internal instance layout for single inheritance with
|
||||||
|
/// large holder type.
|
||||||
|
template <typename T> class huge_unique_ptr {
|
||||||
|
std::unique_ptr<T> ptr;
|
||||||
|
uint64_t padding[10];
|
||||||
|
public:
|
||||||
|
huge_unique_ptr(T *p) : ptr(p) {};
|
||||||
|
T *get() { return ptr.get(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyObject5 { // managed by huge_unique_ptr
|
||||||
|
public:
|
||||||
|
MyObject5(int value) : value{value} {
|
||||||
|
print_created(this);
|
||||||
|
}
|
||||||
|
int value;
|
||||||
|
~MyObject5() {
|
||||||
|
print_destroyed(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// Make pybind aware of the ref-counted wrapper type (s)
|
/// Make pybind aware of the ref-counted wrapper type (s)
|
||||||
|
|
||||||
// ref<T> is a wrapper for 'Object' which uses intrusive reference counting
|
// ref<T> is a wrapper for 'Object' which uses intrusive reference counting
|
||||||
@ -89,6 +111,7 @@ private:
|
|||||||
PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true);
|
PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true);
|
||||||
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>); // Not required any more for std::shared_ptr,
|
PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>); // Not required any more for std::shared_ptr,
|
||||||
// but it should compile without error
|
// but it should compile without error
|
||||||
|
PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr<T>);
|
||||||
|
|
||||||
// Make pybind11 aware of the non-standard getter member function
|
// Make pybind11 aware of the non-standard getter member function
|
||||||
namespace pybind11 { namespace detail {
|
namespace pybind11 { namespace detail {
|
||||||
@ -184,6 +207,10 @@ test_initializer smart_ptr([](py::module &m) {
|
|||||||
.def(py::init<int>())
|
.def(py::init<int>())
|
||||||
.def_readwrite("value", &MyObject4::value);
|
.def_readwrite("value", &MyObject4::value);
|
||||||
|
|
||||||
|
py::class_<MyObject5, huge_unique_ptr<MyObject5>>(m, "MyObject5")
|
||||||
|
.def(py::init<int>())
|
||||||
|
.def_readwrite("value", &MyObject5::value);
|
||||||
|
|
||||||
py::implicitly_convertible<py::int_, MyObject1>();
|
py::implicitly_convertible<py::int_, MyObject1>();
|
||||||
|
|
||||||
// Expose constructor stats for the ref type
|
// Expose constructor stats for the ref type
|
||||||
|
@ -132,6 +132,16 @@ def test_unique_nodelete():
|
|||||||
assert cstats.alive() == 1 # Leak, but that's intentional
|
assert cstats.alive() == 1 # Leak, but that's intentional
|
||||||
|
|
||||||
|
|
||||||
|
def test_large_holder():
|
||||||
|
from pybind11_tests import MyObject5
|
||||||
|
o = MyObject5(5)
|
||||||
|
assert o.value == 5
|
||||||
|
cstats = ConstructorStats.get(MyObject5)
|
||||||
|
assert cstats.alive() == 1
|
||||||
|
del o
|
||||||
|
assert cstats.alive() == 0
|
||||||
|
|
||||||
|
|
||||||
def test_shared_ptr_and_references():
|
def test_shared_ptr_and_references():
|
||||||
from pybind11_tests.smart_ptr import SharedPtrRef, A
|
from pybind11_tests.smart_ptr import SharedPtrRef, A
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user