[Python-Dev] Extending types in C - help needed

Martin v. Loewis martin@v.loewis.de
Fri, 18 Jan 2002 20:36:20 +0100


> Also, the method seems rather complicated for doing a simple thing. The 
> only thing I really want is a way to refer to an _New or _Convert method 
> from Python code.

I believe the attached code implements your requirements. In
particular, see PyArg_GenericCopy for an application that extracts a
void* from an object through a type-safe protocol, then creates a
clone of the original object through the same protocol. Both extractor
and creator function are associated with the type object.

To see this work in Python, run

>>> import handle
>>> x=handle.new(10)
>>> x
<handle.Handle object at 0x81683a0>
>>> y=handle.copy(x)
>>> y
<handle.Handle object at 0x819f270>

Regards,
Martin

#include "Python.h"

/************* Generic Converters ***************/

struct converters{
	PyObject* (*create)(void*);
	int (*extract)(PyObject*, void**);
};

char descr_string[] = "calldll converter structure";

void PyArg_AddConverters(PyTypeObject *type, struct converters* convs)
{
	PyObject *cobj = PyCObject_FromVoidPtrAndDesc(convs, 
						      descr_string,
						      NULL);
	PyDict_SetItemString(type->tp_dict,
			     "__calldll__",
			     cobj);
	Py_DECREF(cobj);
}

struct converters* PyArg_GetConverters(PyTypeObject *type)
{
	PyObject *cobj;
	void *descr;
	cobj = PyObject_GetAttrString((PyObject*)type, "__calldll__");
	if (!cobj)
		return NULL;
	descr = PyCObject_GetDesc(cobj);
	if (!descr)
		return NULL;
	if (descr != descr_string){
		PyErr_SetString(PyExc_TypeError, "invalid cobj");
		return NULL;
	}
	return (struct converters*)PyCObject_AsVoidPtr(cobj);
}

PyObject *PyArg_Create(PyTypeObject* type, void * value)
{
	struct converters *convs = PyArg_GetConverters(type);
	if (!convs)
		return NULL;
	return convs->create(value);
}

int PyArg_Extract(PyObject* obj, void** value)
{
	struct converters *convs = PyArg_GetConverters(obj->ob_type);
	if (!convs)
		return -1;
	convs->extract(obj, value);
	return 0;
}

PyObject* PyArg_GenericCopy(PyObject* obj)
{
	void *tmp;
	if (PyArg_Extract(obj, &tmp))
		return NULL;
	return PyArg_Create(obj->ob_type, tmp);
}

/************* End Generic Converters ***************/


typedef struct {
	PyObject_HEAD
	int handle;
} HandleObject;

staticforward PyTypeObject Handle_Type;

#define HandleObject_Check(v)	((v)->ob_type == &Handle_Type)

static HandleObject *
newHandleObject(int i)
{
	HandleObject *self;
	self = PyObject_New(HandleObject, &Handle_Type);
	if (self == NULL)
		return NULL;
	self->handle = i;
	return self;
}

/* Handle methods */

static void
Handle_dealloc(HandleObject *self)
{
	PyObject_Del(self);
}

/**************** Generic Converters: Handle support ***************/

static PyObject*
handle_conv_new(void *s){
	return (PyObject*)newHandleObject((int)s);
}

static int
handle_conv_extract(PyObject *o, void **dest){
	HandleObject *h = (HandleObject*)o;
	*dest = (void*)h->handle;
	return 0;
}

struct converters HandleConvs = {
	handle_conv_new,
	handle_conv_extract
};

/**************** Generic Converters: Handle support ***************/	

statichere PyTypeObject Handle_Type = {
	/* The ob_type field must be initialized in the module init function
	 * to be portable to Windows without using C++. */
	PyObject_HEAD_INIT(NULL)
	0,			/*ob_size*/
	"handle.Handle",		/*tp_name*/
	sizeof(HandleObject),	/*tp_basicsize*/
	0,			/*tp_itemsize*/
	/* methods */
	(destructor)Handle_dealloc, /*tp_dealloc*/
	0,			/*tp_print*/
	0, /*tp_getattr*/
	0, /*tp_setattr*/
	0,			/*tp_compare*/
	0,			/*tp_repr*/
	0,			/*tp_as_number*/
	0,			/*tp_as_sequence*/
	0,			/*tp_as_mapping*/
	0,			/*tp_hash*/
        0,                      /*tp_call*/
        0,                      /*tp_str*/
        0,                      /*tp_getattro*/
        0,                      /*tp_setattro*/
        0,                      /*tp_as_buffer*/
        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
};
/* --------------------------------------------------------------------- */

static PyObject *
xx_new(PyObject *self, PyObject *args)
{
	HandleObject *rv;
	int h;
	
	if (!PyArg_ParseTuple(args, "i:new", &h))
		return NULL;
	rv = newHandleObject(h);
	if ( rv == NULL )
	    return NULL;
	return (PyObject *)rv;
}

static PyObject *
xx_copy(PyObject *self, PyObject *args)
{
	PyObject *obj;

	if (!PyArg_ParseTuple(args, "O:copy", &obj))
		return NULL;
	return PyArg_GenericCopy(obj);
}

static PyMethodDef xx_methods[] = {
	{"new",		xx_new,		METH_VARARGS},
	{"copy",		xx_copy,		METH_VARARGS},
	{NULL,		NULL}		/* sentinel */
};


DL_EXPORT(void)
inithandle(void)
{
	PyObject *m;

	Handle_Type.ob_type = &PyType_Type;
	PyType_Ready(&Handle_Type);
	PyArg_AddConverters(&Handle_Type, &HandleConvs);

	/* Create the module and add the functions */
	m = Py_InitModule("handle", xx_methods);
}