Skip to content

Commit acbba32

Browse files
committed
Make all classes with the same instance size derive from a common base
In order to fully satisfy Python's inheritance type layout requirements, all types should have a common 'solid' base. A solid base is one which has the same instance size as the derived type (not counting the space required for the optional `dict_ptr` and `weakrefs_ptr`). Thus, `object` does not qualify as a solid base for pybind11 types and this can lead to issues with multiple inheritance. To get around this, new base types are created: one per unique instance size. There is going to be very few of these bases. They ensure Python's MRO checks will pass when multiple bases are involved.
1 parent 4c2599b commit acbba32

File tree

10 files changed

+444
-314
lines changed

10 files changed

+444
-314
lines changed

docs/advanced/classes.rst

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -437,24 +437,15 @@ The section on :ref:`properties` discussed the creation of instance properties
437437
that are implemented in terms of C++ getters and setters.
438438

439439
Static properties can also be created in a similar way to expose getters and
440-
setters of static class attributes. Two things are important to note:
441-
442-
1. Static properties are implemented by instrumenting the *metaclass* of the
443-
class in question -- however, this requires the class to have a modifiable
444-
metaclass in the first place. pybind11 provides a ``py::metaclass()``
445-
annotation that must be specified in the ``class_`` constructor, or any
446-
later method calls to ``def_{property_,∅}_{readwrite,readonly}_static`` will
447-
fail (see the example below).
448-
449-
2. For static properties defined in terms of setter and getter functions, note
450-
that the implicit ``self`` argument also exists in this case and is used to
451-
pass the Python ``type`` subclass instance. This parameter will often not be
452-
needed by the C++ side, and the following example illustrates how to
453-
instantiate a lambda getter function that ignores it:
440+
setters of static class attributes. Note that the implicit ``self`` argument
441+
also exists in this case and is used to pass the Python ``type`` subclass
442+
instance. This parameter will often not be needed by the C++ side, and the
443+
following example illustrates how to instantiate a lambda getter function
444+
that ignores it:
454445

455446
.. code-block:: cpp
456447
457-
py::class_<Foo>(m, "Foo", py::metaclass())
448+
py::class_<Foo>(m, "Foo")
458449
.def_property_readonly_static("foo", [](py::object /* self */) { return Foo(); });
459450
460451
Operator overloading

include/pybind11/attr.h

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,11 @@ struct dynamic_attr { };
5353
/// Annotation which enables the buffer protocol for a type
5454
struct buffer_protocol { };
5555

56-
/// Annotation which requests that a special metaclass is created for a type
57-
struct metaclass { };
56+
/// DEPRECATED: Annotation which requests that a special metaclass is created for a type
57+
struct metaclass {
58+
PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
59+
metaclass() = default;
60+
};
5861

5962
/// Annotation to mark enums as an arithmetic type
6063
struct arithmetic { };
@@ -149,8 +152,7 @@ struct function_record {
149152
/// Special data structure which (temporarily) holds metadata about a bound class
150153
struct type_record {
151154
PYBIND11_NOINLINE type_record()
152-
: multiple_inheritance(false), dynamic_attr(false),
153-
buffer_protocol(false), metaclass(false) { }
155+
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false) { }
154156

155157
/// Handle to the parent scope
156158
handle scope;
@@ -188,9 +190,6 @@ struct type_record {
188190
/// Does the class implement the buffer protocol?
189191
bool buffer_protocol : 1;
190192

191-
/// Does the class require its own metaclass?
192-
bool metaclass : 1;
193-
194193
/// Is the default (unique_ptr) holder type used?
195194
bool default_holder : 1;
196195

@@ -354,11 +353,8 @@ struct process_attribute<buffer_protocol> : process_attribute_default<buffer_pro
354353
static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; }
355354
};
356355

357-
template <>
358-
struct process_attribute<metaclass> : process_attribute_default<metaclass> {
359-
static void init(const metaclass &, type_record *r) { r->metaclass = true; }
360-
};
361-
356+
// DEPRECATED
357+
template <> struct process_attribute<metaclass> : process_attribute_default<metaclass> { };
362358

363359
/// Process an 'arithmetic' attribute for enums (does nothing here)
364360
template <>

include/pybind11/cast.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct type_info {
2626
PyTypeObject *type;
2727
size_t type_size;
2828
void (*init_holder)(PyObject *, const void *);
29+
void (*dealloc)(PyObject *);
2930
std::vector<PyObject *(*)(PyObject *, PyTypeObject *)> implicit_conversions;
3031
std::vector<std::pair<const std::type_info *, void *(*)(void *)>> implicit_casts;
3132
std::vector<bool (*)(PyObject *, void *&)> *direct_conversions;

include/pybind11/class_support.h

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,5 +143,299 @@ inline PyTypeObject* make_default_metaclass() {
143143
return type;
144144
}
145145

146+
extern "C" inline PyObject *pybind11_new(PyTypeObject *type, PyObject *, PyObject *) {
147+
PyObject *self = type->tp_alloc(type, 0);
148+
auto instance = (instance_essentials<void> *) self;
149+
auto tinfo = get_type_info(type);
150+
instance->value = ::operator new(tinfo->type_size);
151+
instance->owned = true;
152+
instance->holder_constructed = false;
153+
get_internals().registered_instances.emplace(instance->value, self);
154+
return self;
155+
}
156+
157+
extern "C" inline int pybind11_init(PyObject *self, PyObject *, PyObject *) {
158+
PyTypeObject *type = Py_TYPE(self);
159+
std::string msg;
160+
#if defined(PYPY_VERSION)
161+
msg += handle((PyObject *) type).attr("__module__").cast<std::string>() + ".";
162+
#endif
163+
msg += type->tp_name;
164+
msg += ": No constructor defined!";
165+
PyErr_SetString(PyExc_TypeError, msg.c_str());
166+
return -1;
167+
}
168+
169+
extern "C" inline void pybind11_dealloc(PyObject *self) {
170+
auto instance = (instance_essentials<void> *) self;
171+
if (instance->value) {
172+
auto type = Py_TYPE(self);
173+
get_type_info(type)->dealloc(self);
174+
175+
auto &registered_instances = get_internals().registered_instances;
176+
auto range = registered_instances.equal_range(instance->value);
177+
bool found = false;
178+
for (auto it = range.first; it != range.second; ++it) {
179+
if (type == Py_TYPE(it->second)) {
180+
registered_instances.erase(it);
181+
found = true;
182+
break;
183+
}
184+
}
185+
if (!found)
186+
pybind11_fail("pybind11_dealloc(): Tried to deallocate unregistered instance!");
187+
188+
if (instance->weakrefs)
189+
PyObject_ClearWeakRefs(self);
190+
191+
PyObject **dict_ptr = _PyObject_GetDictPtr(self);
192+
if (dict_ptr)
193+
Py_CLEAR(*dict_ptr);
194+
}
195+
Py_TYPE(self)->tp_free(self);
196+
}
197+
198+
/** Create a type which can be used as a common base for all classes with the same
199+
instance size, i.e. all classes with the same `sizeof(holder_type)`. This is
200+
needed in order to satisfy Python's requirements for multiple inheritance.
201+
Return value: New reference. */
202+
inline PyObject *make_object_base_type(size_t instance_size) {
203+
auto name = "pybind11_object_" + std::to_string(instance_size);
204+
auto name_obj = reinterpret_steal<object>(PYBIND11_FROM_STRING(name.c_str()));
205+
206+
/* Danger zone: from now (and until PyType_Ready), make sure to
207+
issue no Python C API calls which could potentially invoke the
208+
garbage collector (the GC will call type_traverse(), which will in
209+
turn find the newly constructed type in an invalid state) */
210+
auto metaclass = get_internals().default_metaclass;
211+
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
212+
if (!heap_type)
213+
pybind11_fail("make_object_base_type(): error allocating type!");
214+
215+
heap_type->ht_name = name_obj.inc_ref().ptr();
216+
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
217+
heap_type->ht_qualname = name_obj.inc_ref().ptr();
218+
#endif
219+
220+
auto type = &heap_type->ht_type;
221+
type->tp_name = strdup(name.c_str());
222+
type->tp_base = &PyBaseObject_Type;
223+
type->tp_basicsize = static_cast<ssize_t>(instance_size);
224+
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
225+
226+
type->tp_new = pybind11_new;
227+
type->tp_init = pybind11_init;
228+
type->tp_dealloc = pybind11_dealloc;
229+
230+
/* Support weak references (needed for the keep_alive feature) */
231+
type->tp_weaklistoffset = offsetof(instance_essentials<void>, weakrefs);
232+
233+
if (PyType_Ready(type) < 0)
234+
pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string());
235+
236+
assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
237+
return (PyObject *) heap_type;
238+
}
239+
240+
/** Return the appropriate base type for the given instance size. The results are cached
241+
in `internals.bases` so that only a single base is ever created for any size value.
242+
Return value: Borrowed reference. */
243+
inline PyObject *internals::get_base(size_t instance_size) {
244+
auto it = bases.find(instance_size);
245+
if (it != bases.end()) {
246+
return it->second;
247+
} else {
248+
auto base = make_object_base_type(instance_size);
249+
bases[instance_size] = base;
250+
return base;
251+
}
252+
}
253+
254+
extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
255+
PyObject *&dict = *_PyObject_GetDictPtr(self);
256+
if (!dict)
257+
dict = PyDict_New();
258+
Py_XINCREF(dict);
259+
return dict;
260+
}
261+
262+
extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) {
263+
if (!PyDict_Check(new_dict)) {
264+
PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'",
265+
Py_TYPE(new_dict)->tp_name);
266+
return -1;
267+
}
268+
PyObject *&dict = *_PyObject_GetDictPtr(self);
269+
Py_INCREF(new_dict);
270+
Py_CLEAR(dict);
271+
dict = new_dict;
272+
return 0;
273+
}
274+
275+
static PyGetSetDef pybind11_getset[] = {
276+
{const_cast<char*>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr},
277+
{nullptr, nullptr, nullptr, nullptr, nullptr}
278+
};
279+
280+
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
281+
PyObject *&dict = *_PyObject_GetDictPtr(self);
282+
Py_VISIT(dict);
283+
return 0;
284+
}
285+
286+
extern "C" inline int pybind11_clear(PyObject *self) {
287+
PyObject *&dict = *_PyObject_GetDictPtr(self);
288+
Py_CLEAR(dict);
289+
return 0;
290+
}
291+
292+
extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) {
293+
auto tinfo = get_type_info(Py_TYPE(obj));
294+
if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) {
295+
if (view)
296+
view->obj = nullptr;
297+
PyErr_SetString(PyExc_BufferError, "generic_type::getbuffer(): Internal error");
298+
return -1;
299+
}
300+
memset(view, 0, sizeof(Py_buffer));
301+
buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data);
302+
view->obj = obj;
303+
view->ndim = 1;
304+
view->internal = info;
305+
view->buf = info->ptr;
306+
view->itemsize = (ssize_t) info->itemsize;
307+
view->len = view->itemsize;
308+
for (auto s : info->shape)
309+
view->len *= s;
310+
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
311+
view->format = const_cast<char *>(info->format.c_str());
312+
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
313+
view->ndim = (int) info->ndim;
314+
view->strides = (ssize_t *) &info->strides[0];
315+
view->shape = (ssize_t *) &info->shape[0];
316+
}
317+
Py_INCREF(view->obj);
318+
return 0;
319+
}
320+
321+
extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) {
322+
delete (buffer_info *) view->internal;
323+
}
324+
325+
/** Create a brand new Python type according to the `type_record` specification.
326+
Return value: New reference. */
327+
inline PyObject* make_new_python_type(const type_record &rec) {
328+
auto name = reinterpret_steal<object>(PYBIND11_FROM_STRING(rec.name));
329+
330+
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
331+
auto ht_qualname = name;
332+
if (rec.scope && hasattr(rec.scope, "__qualname__")) {
333+
ht_qualname = reinterpret_steal<object>(
334+
PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr()));
335+
}
336+
#endif
337+
338+
object module;
339+
if (rec.scope) {
340+
if (hasattr(rec.scope, "__module__"))
341+
module = rec.scope.attr("__module__");
342+
else if (hasattr(rec.scope, "__name__"))
343+
module = rec.scope.attr("__name__");
344+
}
345+
346+
#if !defined(PYPY_VERSION)
347+
const auto full_name = module ? str(module).cast<std::string>() + "." + rec.name
348+
: std::string(rec.name);
349+
#else
350+
const auto full_name = std::string(rec.name);
351+
#endif
352+
353+
char *tp_doc = nullptr;
354+
if (rec.doc && options::show_user_defined_docstrings()) {
355+
/* Allocate memory for docstring (using PyObject_MALLOC, since
356+
Python will free this later on) */
357+
size_t size = strlen(rec.doc) + 1;
358+
tp_doc = (char *) PyObject_MALLOC(size);
359+
memcpy((void *) tp_doc, rec.doc, size);
360+
}
361+
362+
auto &internals = get_internals();
363+
auto bases = tuple(rec.bases);
364+
auto base = (bases.size() == 0) ? internals.get_base(rec.instance_size)
365+
: bases[0].ptr();
366+
367+
/* Danger zone: from now (and until PyType_Ready), make sure to
368+
issue no Python C API calls which could potentially invoke the
369+
garbage collector (the GC will call type_traverse(), which will in
370+
turn find the newly constructed type in an invalid state) */
371+
auto metaclass = internals.default_metaclass;
372+
auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0);
373+
if (!heap_type)
374+
pybind11_fail(std::string(rec.name) + ": Unable to create type object!");
375+
376+
heap_type->ht_name = name.release().ptr();
377+
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
378+
heap_type->ht_qualname = ht_qualname.release().ptr();
379+
#endif
380+
381+
auto type = &heap_type->ht_type;
382+
type->tp_name = strdup(full_name.c_str());
383+
type->tp_doc = tp_doc;
384+
type->tp_base = (PyTypeObject *) handle(base).inc_ref().ptr();
385+
type->tp_basicsize = static_cast<ssize_t>(rec.instance_size);
386+
if (bases.size() > 0)
387+
type->tp_bases = bases.release().ptr();
388+
389+
/* Supported protocols */
390+
type->tp_as_number = &heap_type->as_number;
391+
type->tp_as_sequence = &heap_type->as_sequence;
392+
type->tp_as_mapping = &heap_type->as_mapping;
393+
394+
/* Flags */
395+
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
396+
#if PY_MAJOR_VERSION < 3
397+
type->tp_flags |= Py_TPFLAGS_CHECKTYPES;
398+
#endif
399+
400+
/* Support dynamic attributes */
401+
if (rec.dynamic_attr) {
402+
#if defined(PYPY_VERSION)
403+
pybind11_fail(std::string(rec.name) + ": dynamic attributes are "
404+
"currently not supported in "
405+
"conjunction with PyPy!");
406+
#endif
407+
type->tp_dictoffset = type->tp_basicsize; // place dict at the end
408+
type->tp_basicsize += sizeof(PyObject *); // and allocate enough space for it
409+
type->tp_getset = pybind11_getset;
410+
type->tp_traverse = pybind11_traverse;
411+
type->tp_clear = pybind11_clear;
412+
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
413+
}
414+
415+
if (rec.buffer_protocol) {
416+
type->tp_as_buffer = &heap_type->as_buffer;
417+
#if PY_MAJOR_VERSION < 3
418+
type->tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;
419+
#endif
420+
heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer;
421+
heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer;
422+
}
423+
424+
if (PyType_Ready(type) < 0)
425+
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!");
426+
427+
assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)
428+
: !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC));
429+
430+
/* Register type with the parent scope */
431+
if (rec.scope)
432+
setattr(rec.scope, rec.name, (PyObject *) type);
433+
434+
if (module) // Needed by pydoc
435+
setattr((PyObject *) type, "__module__", module);
436+
437+
return (PyObject *) type;
438+
}
439+
146440
NAMESPACE_END(detail)
147441
NAMESPACE_END(pybind11)

0 commit comments

Comments
 (0)