PyCObject_FromVoidPtr etc.

F

Fons Adriaensen

Hello all,

Upgrading to 3.2 has provided some sort of cold shower.

I have hundreds of extensions that fail to install due to
PyCObject_FromVoidPtr() and PyCObject_AsVoidPtr() not longer
being available, and I'm looking for a (hopefully) simple
solution.

In all cases, these are extensions that create an instance
of a C++ class which is purely a 'slave' to the corresponding
instance of a Python class.

They all follow a pattern illustrated by the following code:


-------
#include "class_XX.h"


extern "C" void destroy (void *object)
{
Class_XX *C;

C = (Class_XX *) object;
delete P;
}


extern "C" PyObject* create (PyObject *self, PyObject *args)
{
Class_XX *C;
PyObject *P;
int a, b;

if (! PyArg_ParseTuple(args, "(Oii)", &P, &a, &b)) return NULL;
C = new Class_XX (P, a, b);
return PyCObject_FromVoidPtr((void *) C, destroy);
}


extern "C" PyObject* method_1 (PyObject *self, PyObject *args)
{
Class_XX *J;
PyObject *P;
int x, y;

if (! PyArg_ParseTuple(args, "(Oii)", &P, &x, &y)) return NULL;
C = (Class_XX *) PyCObject_AsVoidPtr(P);
P = Py_BuildValue ("(i)", C->method_1 (x, y));
Py_INCREF (P);
return P;
}

....

------


The C++ object stores a pointer to its Python equivalent (not needed
in most cases, only if there would be callbacks) and the Python object
stores a PyCObject corresponding to its C++ slave and uses this to call
its methods.

In no case there is any need of the C++ objects being visible anywhere
else, let alone in other extensions.

Is there a way to keep things (almost) as simple as this using the
'Capsules' ?? I do understand the advantage of having such a mechanism,
but in this case it's sort of overkill...

TIA,
 
M

Martin v. Loewis

Is there a way to keep things (almost) as simple as this using the
'Capsules' ??

Most certainly. Instead of PyCObject_FromVoidPtr, use PyCapsule_New.
Either pass NULL as a name, or the class name for additional
type-safety. Instead of PyCObject_AsVoidPtr, use PyCapsule_GetPointer.
The only difference will be a change to the destructor function:
it gets the capsule now, rather than the embedded pointer.
I do understand the advantage of having such a mechanism,
but in this case it's sort of overkill...

It's the very same mechanism. The only difference is that capsules
are type-safe where CObjects were not. I.e. you can crash the
interpreter by passing in a bad CObject (one for a different type);
with capsules, this can be detected and a Python exception be raised.

Because of the additional type-safety, additional parameters are
needed, and because of that, a new name is needed. Nothing else
has changed.

In fact, you can do:

#if Python-version >= 3.2 (proper test left to the reader)
#define PyCObject_FromVoidPtr(C, ignored_destroy)
PyCapsule_New(C, "mymod.Class_XX", destroy_32)
#define PyCObject_AsVoidPtr(P) PyCapsule_GetPointer(P, "mymod.Class_XX")
extern "C" void destroy_32(PyObject *P){
destroy(PyCapsule_GetPointer(P, "mymod.Class_XX"));
}
#endif

This wouldn't do proper type-checking yet - all callers
of PyCObject_AsVoidPtr then should add

if (C == NULL) return NULL;

which still should be backwards-compatible with 3.1 and earlier
(in which versions you actually also should check for NULL, since
even PyCObject_AsVoidPointer can fail, esp. if you are passing
something that isn't a PyCObject).

Regards,
Martin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top