Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expose an underlying struct as a member of a custom type within a Python extension

Tags:

python

c

struct

This question is a slight spin on a previous question: Accessing the underlying struct of a PyObject

Except in my version I want to know how to expose the fields of the Point struct as members of my new type.

I have looked everywhere I could think, read numerous example Python extensions and read many tutorials, docs, etc. and have not been able to find a clean way to do this.

Below is an example of what I want to do. I have some internal struct that I would like to expose via the Python extension but I don't want to have to redefine the struct in my code. It seems like the main area that is the problem is the PyMemeberDef definition...what would the offset be of x or y inside the Point struct from the context of the PointObject struct?

Any help is much appreciated, thanks.

#include <Python.h>
#include <structmember.h>

// This is actually defined elsewhere in someone else's code.
struct Point {
 int x;
 int y;
};

struct PointObject {
 PyObject_HEAD
 struct Point* my_point;
 int z;
};

static PyMemberDef point_members[] = {
    {"z", T_INT, offsetof(struct PointObject, z), 0, "z field"},
    {"x", T_INT, offsetof(???), 0, "point x field"},
    {"y", T_INT, offsetof(???), 0, "point y field"},
    {NULL}
};

static PyTypeObject PointType = {
    PyObject_HEAD_INIT(NULL)
    .tp_name = "Point",
    .tp_basicsize = sizeof(PointObject),
    .tp_flags = Py_TPFLAGS_DEFAULT,
    .tp_doc = "Point objects",
    .tp_members = point_members,
    .tp_init = (initproc)point_init,
};

...

PyMODINIT_FUNC
initMainModule(void)
{
    PyObject *m = Py_InitModule(MODULE, NULL);

    // Register PointType
    PointType.tp_new = PyType_GenericNew;
    if (PyType_Ready(&PointType) < 0)
            return;
    Py_INCREF(&PointType);
    PyModule_AddObject(m, "Point", (PyObject *)&PointType);

    ...
}
like image 862
Jeff Avatar asked Nov 04 '22 16:11

Jeff


1 Answers

I would highly recommend reading http://docs.python.org/release/2.6/_sources/extending/newtypes.txt, specifically the section on "Providing finer control over data attributes".

After reading this, it appears it is possible to do this by defining a collection of setter/getters for the specific members in the Point struct. It does seem a bit overkill for something as simple as this, but it appears to work just fine.

Below is an example on how to do it for the 'x' field of Point, based on adding the following code to my previous example:

static PyGetSetDef point_getset[] = {
  {"x", (getter)point_get_x, (setter)point_set_x, "", NULL},
  {NULL}
};

static PyObject*
point_get_x(struct PointObject *self, void *closure)
{
     return Py_BuildValue("i", self->my_point->x);
}

static int
point_set_x(struct PointObject *self, PyObject *value, void *closure)
{
  PyErr_SetString(PyExc_TypeError, "Attribute is read-only!");
  return -1;
}

Lastly, add the point_getset struct to the tp_getset field in the previously defined PyTypeObject struct. It should be noted that you can still define members in the traditional PyMemberDef struct for the simple cases.

like image 171
Jeff Avatar answered Nov 15 '22 06:11

Jeff