Skip to content

Commit 3154dc3

Browse files
committed
Document PyTypeObject functions
1 parent 9c8d2d5 commit 3154dc3

File tree

1 file changed

+49
-27
lines changed

1 file changed

+49
-27
lines changed

include/pybind11/class_support.h

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ inline PyTypeObject* make_default_metaclass() {
143143
return type;
144144
}
145145

146+
/// Instance creation function for all pybind11 types. It only allocates space for the
147+
/// C++ object, but doesn't call the constructor -- an `__init__` function must do that.
146148
extern "C" inline PyObject *pybind11_new(PyTypeObject *type, PyObject *, PyObject *) {
147149
PyObject *self = type->tp_alloc(type, 0);
148150
auto instance = (instance_essentials<void> *) self;
@@ -154,6 +156,9 @@ extern "C" inline PyObject *pybind11_new(PyTypeObject *type, PyObject *, PyObjec
154156
return self;
155157
}
156158

159+
/// An `__init__` function constructs the C++ object. Users should provide at least one
160+
/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the
161+
/// following default function will be used which simply throws an exception.
157162
extern "C" inline int pybind11_init(PyObject *self, PyObject *, PyObject *) {
158163
PyTypeObject *type = Py_TYPE(self);
159164
std::string msg;
@@ -166,6 +171,8 @@ extern "C" inline int pybind11_init(PyObject *self, PyObject *, PyObject *) {
166171
return -1;
167172
}
168173

174+
/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc`
175+
/// to destroy the C++ object itself, while the rest is Python bookkeeping.
169176
extern "C" inline void pybind11_dealloc(PyObject *self) {
170177
auto instance = (instance_essentials<void> *) self;
171178
if (instance->value) {
@@ -251,6 +258,7 @@ inline PyObject *internals::get_base(size_t instance_size) {
251258
}
252259
}
253260

261+
/// dynamic_attr: Support for `d = instance.__dict__`.
254262
extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
255263
PyObject *&dict = *_PyObject_GetDictPtr(self);
256264
if (!dict)
@@ -259,6 +267,7 @@ extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) {
259267
return dict;
260268
}
261269

270+
/// dynamic_attr: Support for `instance.__dict__ = dict()`.
262271
extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) {
263272
if (!PyDict_Check(new_dict)) {
264273
PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'",
@@ -272,23 +281,42 @@ extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void
272281
return 0;
273282
}
274283

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-
284+
/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`.
280285
extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) {
281286
PyObject *&dict = *_PyObject_GetDictPtr(self);
282287
Py_VISIT(dict);
283288
return 0;
284289
}
285290

291+
/// dynamic_attr: Allow the GC to clear the dictionary.
286292
extern "C" inline int pybind11_clear(PyObject *self) {
287293
PyObject *&dict = *_PyObject_GetDictPtr(self);
288294
Py_CLEAR(dict);
289295
return 0;
290296
}
291297

298+
/// Give instances of this type a `__dict__` and opt into garbage collection.
299+
inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) {
300+
auto type = &heap_type->ht_type;
301+
#if defined(PYPY_VERSION)
302+
pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are "
303+
"currently not supported in "
304+
"conjunction with PyPy!");
305+
#endif
306+
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
307+
type->tp_dictoffset = type->tp_basicsize; // place dict at the end
308+
type->tp_basicsize += sizeof(PyObject *); // and allocate enough space for it
309+
type->tp_traverse = pybind11_traverse;
310+
type->tp_clear = pybind11_clear;
311+
312+
static PyGetSetDef getset[] = {
313+
{const_cast<char*>("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr},
314+
{nullptr, nullptr, nullptr, nullptr, nullptr}
315+
};
316+
type->tp_getset = getset;
317+
}
318+
319+
/// buffer_protocol: Fill in the view as specified by flags.
292320
extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) {
293321
auto tinfo = get_type_info(Py_TYPE(obj));
294322
if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) {
@@ -318,10 +346,22 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
318346
return 0;
319347
}
320348

349+
/// buffer_protocol: Release the resources of the buffer.
321350
extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) {
322351
delete (buffer_info *) view->internal;
323352
}
324353

354+
/// Give this type a buffer interface.
355+
inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) {
356+
heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer;
357+
#if PY_MAJOR_VERSION < 3
358+
heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;
359+
#endif
360+
361+
heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer;
362+
heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer;
363+
}
364+
325365
/** Create a brand new Python type according to the `type_record` specification.
326366
Return value: New reference. */
327367
inline PyObject* make_new_python_type(const type_record &rec) {
@@ -399,29 +439,11 @@ inline PyObject* make_new_python_type(const type_record &rec) {
399439
type->tp_flags |= Py_TPFLAGS_CHECKTYPES;
400440
#endif
401441

402-
/* Support dynamic attributes */
403-
if (rec.dynamic_attr) {
404-
#if defined(PYPY_VERSION)
405-
pybind11_fail(std::string(rec.name) + ": dynamic attributes are "
406-
"currently not supported in "
407-
"conjunction with PyPy!");
408-
#endif
409-
type->tp_dictoffset = type->tp_basicsize; // place dict at the end
410-
type->tp_basicsize += sizeof(PyObject *); // and allocate enough space for it
411-
type->tp_getset = pybind11_getset;
412-
type->tp_traverse = pybind11_traverse;
413-
type->tp_clear = pybind11_clear;
414-
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
415-
}
442+
if (rec.dynamic_attr)
443+
enable_dynamic_attributes(heap_type);
416444

417-
if (rec.buffer_protocol) {
418-
type->tp_as_buffer = &heap_type->as_buffer;
419-
#if PY_MAJOR_VERSION < 3
420-
type->tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER;
421-
#endif
422-
heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer;
423-
heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer;
424-
}
445+
if (rec.buffer_protocol)
446+
enable_buffer_protocol(heap_type);
425447

426448
if (PyType_Ready(type) < 0)
427449
pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!");

0 commit comments

Comments
 (0)