I have a yec.c file defining a structure with two functions:
#include <python2.7/Python.h>
struct mec
{
int age;
int number;
};
static PyObject* nopoint(PyObject* self, PyObject* args)
{
struct mec m;
int n1, n2;
if (!PyArg_ParseTuple(args, "ii", &n1, &n2))
return NULL;
printf("nopoint(c) nombres: %d et %d!\n", n1, n2);
m.age = n1;
m.number = n2;
printf("nopoint(c) age nb: %d et %d!\n", m.age, m.number);
return Py_BuildValue("i", n1 + n2);
}
static PyObject* viapoint(PyObject* self, PyObject* args)
{
struct mec *m;
if (!PyArg_ParseTuple(args, "o", &m))
return NULL;
printf("viapoint av(c) age nb: %d et %d!\n", m->age, m->number);
m->age = 10;
m->number = 1;
printf("viapoint ap(c) age nb: %d et %d!\n", m->age, m->number);
return Py_BuildValue("i", m->age + m->number);
}
static PyMethodDef MyYecMethods[] = {
{"nopoint", nopoint, METH_VARARGS, "Description de fune"},
{"viapoint", viapoint, METH_VARARGS, "Description de fdeux"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
inityec(void)
{
(void) Py_InitModule("yec", MyYecMethods);
}
I compiled the yec.c file into yec.so with a python setup_yec.py build command on the following setup_yec.py file:
from distutils.core import setup, Extension
module1 = Extension('yec', sources = ['yec.c'])
setup (name = 'YecPkg',
version = '1.0',
description = 'This is a demo of yec pkg',
ext_modules = [module1])
I can use my compiled library under Python and the nopoint() function works:
import yec
yec.nopoint(3, 4)
I would like to use the second function ; viapoint() of my library which should accepts a struct pointer from Python where I define the related ctypes.Structure:
from ctypes import *
class Mec(Structure):
_fields_ = [("age", c_int),
("number", c_int)]
m = Mec(1, 2)
print "py mec class", m.age, m.number
yec.viapoint(byref(m))
Of course, it does not work:
Traceback (most recent call last):
File "testyec.py", line 18, in <module>
yec.viapoint(byref(m))
TypeError: must be impossible<bad format char>, not CArgObject
If someone knows how to modify the viapoint() function to be able to parse the structure pointer via the PyArg_ParseTuple() and how to pass the python structure pointer in python (using byref?), it would be a great help.
Thanks.
You can parse the Structure as a read-write buffer ("w#"). By passing it as an argument you can rest assured that it's a referenced object. It also ensures that the passed in buffer is writable memory of the correct size. Crashing Python is not acceptable. You're supposed to get exceptions in Python. If you have Python code that makes it trivial to segfault the interpreter, you're doing it very wrong.
static PyObject* viapoint(PyObject* self, PyObject* args)
{
struct mec *m;
size_t size;
if (!PyArg_ParseTuple(args, "w#", &m, &size))
return NULL;
if (size != sizeof(struct mec)) {
PyErr_SetString(PyExc_TypeError, "wrong buffer size");
return NULL;
}
printf("viapoint av(c) age nb: %d et %d!\n", m->age, m->number);
m->age = 10;
m->number = 1;
return Py_BuildValue("i", m->age + m->number);
}
Python:
from ctypes import *
import yec
class Mec(Structure):
_fields_ = [
("age", c_int),
("number", c_int),
]
class Bad(Structure):
_fields_ = [
("age", c_int),
("number", c_int),
("extra", c_int),
]
m = Mec(1, 2)
print yec.viapoint(m)
# TypeError
b = Bad(1, 2, 3)
print yec.viapoint(b)
If you just accept an address as the argument, your function might segfault on an invalid pointer, or just return garbage or modify memory that will make you program crash later in an inscrutable way. Moreover, by parsing the address you'll need to conditionally define whether to parse as long or long long in the preprocessor, depending on the size of void * compared to long. For example, on Win64 a long is 32bit and parsing a pointer as long truncates it. Finally, an API that requires you to first call addressof in Python is an inefficient kludge.
You need to use ctypes.addressof from the Python script, rather than ctypes.byref (which is a different object from a C pointer), and then, in yec.c, parse the input value as a long (or int if in 32bits) and assign it to the "struct mec *".
See below a working example:
#include <python2.7/Python.h>
struct mec
{
int age;
int number;
};
static PyObject* nopoint(PyObject* self, PyObject* args)
{
struct mec m;
int n1, n2;
if (!PyArg_ParseTuple(args, "ii", &n1, &n2))
return NULL;
printf("nopoint(c) nombres: %d et %d!\n", n1, n2);
m.age = n1;
m.number = n2;
printf("nopoint(c) age nb: %d et %d!\n", m.age, m.number);
return Py_BuildValue("i", n1 + n2);
}
static PyObject* viapoint(PyObject* self, PyObject* args)
{
struct mec *m;
if (!PyArg_ParseTuple(args, "l", &m))
return NULL;
printf("viapoint av(c) age nb: %d et %d!\n", m->age, m->number);
m->age = 10;
m->number = 1;
printf("viapoint ap(c) age nb: %d et %d!\n", m->age, m->number);
return Py_BuildValue("i", m->age + m->number);
}
static PyMethodDef MyYecMethods[] = {
{"nopoint", nopoint, METH_VARARGS, "Description de fune"},
{"viapoint", viapoint, METH_VARARGS, "Description de fdeux"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
inityec(void)
{
(void) Py_InitModule("yec", MyYecMethods);
}
and in Python:
from ctypes import *
import yec
class Mec(Structure):
_fields_ = [
("age", c_int),
("number", c_int)]
m = Mec(1, 2)
print "py mec class", m.age, m.number
yec.viapoint(addressof(m))
Running it, I get:
> python run.py
py mec class 1 2
viapoint av(c) age nb: 1 et 2!
viapoint ap(c) age nb: 10 et 1!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With