@@ -250,16 +250,17 @@ Because we now have data to manage, we have to be more careful about object
250250allocation and deallocation. At a minimum, we need a deallocation method::
251251
252252 static void
253- Custom_dealloc(CustomObject *self )
253+ Custom_dealloc(PyObject *op )
254254 {
255+ CustomObject *self = (CustomObject *) op;
255256 Py_XDECREF(self->first);
256257 Py_XDECREF(self->last);
257- Py_TYPE(self)->tp_free((PyObject *) self);
258+ Py_TYPE(self)->tp_free(self);
258259 }
259260
260261which is assigned to the :c:member: `~PyTypeObject.tp_dealloc ` member::
261262
262- .tp_dealloc = (destructor) Custom_dealloc,
263+ .tp_dealloc = Custom_dealloc,
263264
264265This method first clears the reference counts of the two Python attributes.
265266:c:func: `Py_XDECREF ` correctly handles the case where its argument is
@@ -270,11 +271,31 @@ the object's type might not be :class:`!CustomType`, because the object may
270271be an instance of a subclass.
271272
272273.. note ::
273- The explicit cast to ``destructor `` above is needed because we defined
274- ``Custom_dealloc `` to take a ``CustomObject * `` argument, but the ``tp_dealloc ``
275- function pointer expects to receive a ``PyObject * `` argument. Otherwise,
276- the compiler will emit a warning. This is object-oriented polymorphism,
277- in C!
274+
275+ The explicit cast to ``CustomObject * `` above is needed because we defined
276+ ``Custom_dealloc `` to take a ``PyObject * `` argument, as the ``tp_dealloc ``
277+ function pointer expects to receive a ``PyObject * `` argument.
278+ By assigning to the the ``tp_dealloc `` slot of a type, we declare
279+ that it can only be called with instances of our ``CustomObject ``
280+ class, so the cast to ``(CustomObject *) `` is safe.
281+ This is object-oriented polymorphism, in C!
282+
283+ In existing code, or in previous versions of this tutorial,
284+ you might see similar functions take a pointer to the subtype
285+ object structure (``CustomObject* ``) directly, like this::
286+
287+ Custom_dealloc(CustomObject *self)
288+ {
289+ Py_XDECREF(self->first);
290+ Py_XDECREF(self->last);
291+ Py_TYPE(self)->tp_free((PyObject *) self);
292+ }
293+ ...
294+ .tp_dealloc = (destructor) Custom_dealloc,
295+
296+ This does the same thing on all architectures that CPython
297+ supports, but according to the C standard, it invokes
298+ undefined behavior.
278299
279300We want to make sure that the first and last names are initialized to empty
280301strings, so we provide a ``tp_new `` implementation::
@@ -352,8 +373,9 @@ We also define an initialization function which accepts arguments to provide
352373initial values for our instance::
353374
354375 static int
355- Custom_init(CustomObject *self , PyObject *args, PyObject *kwds)
376+ Custom_init(PyObject *op , PyObject *args, PyObject *kwds)
356377 {
378+ CustomObject *self = (CustomObject *) op;
357379 static char *kwlist[] = {"first", "last", "number", NULL};
358380 PyObject *first = NULL, *last = NULL, *tmp;
359381
@@ -379,7 +401,7 @@ initial values for our instance::
379401
380402by filling the :c:member: `~PyTypeObject.tp_init ` slot. ::
381403
382- .tp_init = (initproc) Custom_init,
404+ .tp_init = Custom_init,
383405
384406The :c:member: `~PyTypeObject.tp_init ` slot is exposed in Python as the
385407:meth: `~object.__init__ ` method. It is used to initialize an object after it's
@@ -451,8 +473,9 @@ We define a single method, :meth:`!Custom.name`, that outputs the objects name a
451473concatenation of the first and last names. ::
452474
453475 static PyObject *
454- Custom_name(CustomObject *self , PyObject *Py_UNUSED(ignored ))
476+ Custom_name(PyObject *op , PyObject *Py_UNUSED(dummy ))
455477 {
478+ CustomObject *self = (CustomObject *) op;
456479 if (self->first == NULL) {
457480 PyErr_SetString(PyExc_AttributeError, "first");
458481 return NULL;
@@ -486,7 +509,7 @@ Now that we've defined the method, we need to create an array of method
486509definitions::
487510
488511 static PyMethodDef Custom_methods[] = {
489- {"name", (PyCFunction) Custom_name, METH_NOARGS,
512+ {"name", Custom_name, METH_NOARGS,
490513 "Return the name, combining the first and last name"
491514 },
492515 {NULL} /* Sentinel */
@@ -543,15 +566,17 @@ we'll use custom getter and setter functions. Here are the functions for
543566getting and setting the :attr: `!first ` attribute::
544567
545568 static PyObject *
546- Custom_getfirst(CustomObject *self , void *closure)
569+ Custom_getfirst(PyObject *op , void *closure)
547570 {
571+ CustomObject *self = (CustomObject *) op;
548572 Py_INCREF(self->first);
549573 return self->first;
550574 }
551575
552576 static int
553- Custom_setfirst(CustomObject *self , PyObject *value, void *closure)
577+ Custom_setfirst(PyObject *op , PyObject *value, void *closure)
554578 {
579+ CustomObject *self = (CustomObject *) op;
555580 PyObject *tmp;
556581 if (value == NULL) {
557582 PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
@@ -583,9 +608,9 @@ new value is not a string.
583608We create an array of :c:type: `PyGetSetDef ` structures::
584609
585610 static PyGetSetDef Custom_getsetters[] = {
586- {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
611+ {"first", Custom_getfirst, Custom_setfirst,
587612 "first name", NULL},
588- {"last", (getter) Custom_getlast, (setter) Custom_setlast,
613+ {"last", Custom_getlast, Custom_setlast,
589614 "last name", NULL},
590615 {NULL} /* Sentinel */
591616 };
@@ -609,8 +634,9 @@ We also need to update the :c:member:`~PyTypeObject.tp_init` handler to only
609634allow strings [# ]_ to be passed::
610635
611636 static int
612- Custom_init(CustomObject *self , PyObject *args, PyObject *kwds)
637+ Custom_init(PyObject *op , PyObject *args, PyObject *kwds)
613638 {
639+ CustomObject *self = (CustomObject *) op;
614640 static char *kwlist[] = {"first", "last", "number", NULL};
615641 PyObject *first = NULL, *last = NULL, *tmp;
616642
@@ -689,8 +715,9 @@ First, the traversal method lets the cyclic GC know about subobjects that could
689715participate in cycles::
690716
691717 static int
692- Custom_traverse(CustomObject *self , visitproc visit, void *arg)
718+ Custom_traverse(PyObject *op , visitproc visit, void *arg)
693719 {
720+ CustomObject *self = (CustomObject *) op;
694721 int vret;
695722 if (self->first) {
696723 vret = visit(self->first, arg);
@@ -716,8 +743,9 @@ functions. With :c:func:`Py_VISIT`, we can minimize the amount of boilerplate
716743in ``Custom_traverse ``::
717744
718745 static int
719- Custom_traverse(CustomObject *self , visitproc visit, void *arg)
746+ Custom_traverse(PyObject *op , visitproc visit, void *arg)
720747 {
748+ CustomObject *self = (CustomObject *) op;
721749 Py_VISIT(self->first);
722750 Py_VISIT(self->last);
723751 return 0;
@@ -731,8 +759,9 @@ Second, we need to provide a method for clearing any subobjects that can
731759participate in cycles::
732760
733761 static int
734- Custom_clear(CustomObject *self )
762+ Custom_clear(PyObject *op )
735763 {
764+ CustomObject *self = (CustomObject *) op;
736765 Py_CLEAR(self->first);
737766 Py_CLEAR(self->last);
738767 return 0;
@@ -765,11 +794,11 @@ Here is our reimplemented deallocator using :c:func:`PyObject_GC_UnTrack`
765794and ``Custom_clear ``::
766795
767796 static void
768- Custom_dealloc(CustomObject *self )
797+ Custom_dealloc(PyObject *op )
769798 {
770- PyObject_GC_UnTrack(self );
771- Custom_clear(self );
772- Py_TYPE(self )->tp_free((PyObject *) self );
799+ PyObject_GC_UnTrack(op );
800+ (void) Custom_clear(op );
801+ Py_TYPE(op )->tp_free(op );
773802 }
774803
775804Finally, we add the :c:macro: `Py_TPFLAGS_HAVE_GC ` flag to the class flags::
@@ -825,9 +854,10 @@ When a Python object is a :class:`!SubList` instance, its ``PyObject *`` pointer
825854can be safely cast to both ``PyListObject * `` and ``SubListObject * ``::
826855
827856 static int
828- SubList_init(SubListObject *self , PyObject *args, PyObject *kwds)
857+ SubList_init(PyObject *op , PyObject *args, PyObject *kwds)
829858 {
830- if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
859+ SubListObject *self = (SubListObject *) op;
860+ if (PyList_Type.tp_init(op, args, kwds) < 0)
831861 return -1;
832862 self->state = 0;
833863 return 0;
0 commit comments