[capi-sig] A special kind of abstract base class - will that work ?

Joost joost at h-labahn.de
Sat Nov 21 09:46:38 CET 2009


Hello,

With depikt, my python wrappers for gtk i am
working at now, i posed a cry of help here, which
wasn't needed anymore some days later (but perhaps
any kind of utterance was indispensable for
continuing). The problem is to represent
gtk's kind of "class hierarchy" in Python. This here
is my solution, it seems to work and given this is
stable it will be the final version.

depikt uses one abstract base class Pikt. Pikt is
nothing than its instance and its type struct - and
that is the point of my question. As follows:

typedef struct { PyObject_HEAD   PyObject *gobj; } Pikt;
static PyTypeObject Pikt_Type = {
	PyVarObject_HEAD_INIT(NULL, 0)
	"depikt.Pikt",
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,					
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
};

That is all concerning Pikt. gobj is there to contain
the PyCapsules, which itself contain the gtk-pointers
to the widgets, created by the following preprocessor
factory:

#define KT__GOB_HEADER(YYY, newfunc)\
typedef struct { Pikt Q; } YYY;\
static PyTypeObject YYY##_Type;\
  \
PyObject *YYY##_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {\
	YYY *self; self = (YYY *)type->tp_alloc(type, 0); \
	self->Q.gobj = PyCapsule_New(newfunc, #YYY, NULL); \
	if (! self->Q.gobj) { Py_DECREF(self); KTERR("Cannot create "#YYY);};\
	Py_INCREF(self); return (PyObject *)self;\
};

#define KT__GOB_TYPE(XXX)  \
\
void XXX##_free(XXX *self) \
	{ PyObject_Del(self); };\
void XXX##_dealloc(XXX *self)\
	{ Py_XDECREF(self); g_free((gpointer)self->Q.gobj); XXX##_free(self); };\
\
static PyTypeObject XXX##_Type = {\
	PyVarObject_HEAD_INIT(NULL, 0)\
	"depikt."#XXX,\
	sizeof(XXX)+4*sizeof(NULL),\
	0,\
	(destructor)XXX##_dealloc,\
	0,\
	0, \
	0, 0, 0, 0, 0, 0, 0, 0, 0,\
	0,\
	0,\
	0,\
	Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,\
         "Python type object for "#XXX,\
         0, 0, 0, 0, 0, 0,\
         XXX##_methods,\
         0, 0,\
	&Pikt_Type,\
	0,\
	0, 0,\
	-4*sizeof(NULL),\
	0, 0,\
         XXX##_new,\
	(freefunc)XXX##_free,\
};

The headers are only for those widgets, for which literally constant
newfuncs can be provided (like gtk_window_new(GTK_WINDOW_TOPLEVEL).

(Besides: I am still unsure, whether the dealloc/free couple is right
here).

All widgets shall also consist only of the gtk-pointers returned from
those newfuncs and method tables - no attributes. The intermediate
abstract widgets as GtkContainer are realized as method bundles
getting added to each concrete widget's method table by the preprocessor.

Pikt is indsipensable as the argument type for

	depikt_Container_add(Pikt *self, Pikt *to_add)

for example. Thus this abstract class is not there for providing a
protocol, but as kind of stub for the type system. It is not exposed
to Python and actually no proper class.

This looks very clean and petty to me - still i am amazed to have found
this. It might even be useful as a design pattern for other extensions
(thus i hope, you do not feel like wasting your time reading this).
But this goes far beyond of what is teached in the Python tutorials and
for me a painful doubt is remaining, whether Pikt as this will really
always work.

C combined with inheritance by casting itself and the Python C-API even
more have so terrible side effects of allocation strategies. All the more,
as i still produce bad programming errors - this solution has by no means
solved all my problems with depikt (this is my first ambitious C-project).
But already depikt has features pygtk has not.

In this form my question is probably not decidable, but still i'd like
to have an estimation of the involved risks doing it like this.

Thanks for reading, Joost


More information about the capi-sig mailing list