2014-01-09 23 views
2

我有一个yec.c文件中定义的结构与两种功能的C语言结构指针:通行证从使用Python ctypes的

#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); 
} 

我的yec.c文件与python setup_yec.py build命令编译成yec.so以下setup_yec.py文件:

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]) 

我可以使用Python和nopoint()函数的工作在我的编译库:

import yec 
yec.nopoint(3, 4) 

我想用第二个函数;它应该接受来自Python中的结构指针,我定义了相关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)) 

。当然,这是行不通的:

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 

如果有人知道如何修改viapoint()函数以便能够通过PyArg_ParseTuple()解析结构指针,以及如何在Python中使用python结构指针(使用byref?),这将是一个很大的帮助。

谢谢。

回答

3

可以解析Structure为只读写缓冲区("w#")。通过将它作为参数传递,您可以放心,它是一个引用的对象。它还确保传入的缓冲区是正确大小的可写内存。崩溃的Python是不可接受的。你应该在Python中获取异常。如果你有Python代码让代码解释器变得微不足道,那么你就错了。

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) 

如果你只是接受地址作为参数,你的功能可能会出现段错误的无效指针,或只返回垃圾或修改内存,这将使你的程序后来以一种不可思议的方式崩溃。此外,通过解析地址,您需要有条件地定义在预处理器中是否解析为longlong long,具体取决于与long相比的void *的大小。例如,在Win64上,一个long是32位并解析一个指针,因为long会截断它。最后,要求你首先在Python中调用addressof的API是一个效率低下的混乱。

+0

好吧,使用读写缓冲方法更安全!我会改变我的代码。谢谢。 – user1520280

+0

如果我将在C中创建结构,并通过c库中的函数返回它的指针。我只是简单地将它存储在python中的变量中,或者还有另一种方法可以在python中使用它,如你所描述的那样? – user1520280

+0

我的答案中的方法仅适用于实现具有可写缓冲区的缓冲区接口的Python对象。您可以将它与ctypes数据类型或NumPy数组一起使用,等等。通过Python走私指针的最好方法是将其封装在“PyCapsule”中。说你'malloc'结构的内存。然后用一个析构函数创建胶囊,在指针上调用'free'。 – eryksun

2

您需要使用ctypes.addressof从Python脚本,而不是ctypes.byref(这是从C的指针不同的对象),然后,在yec.c,解析输入值作为long(或int如果在32位)和将其分配给“struct mec *”。

看到一个工作示例如下:

#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); 
} 

,并在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)) 

运行它,我得到:

> python run.py                     
py mec class 1 2 
viapoint av(c) age nb: 1 et 2! 
viapoint ap(c) age nb: 10 et 1! 
+0

完美,它的工作原理。与读写缓冲区(“w#”)相比,我更容易使用此解决方案。我想知道它是否会以某种方式使用指针或读写缓冲区选项有所作为?偶然的机会,我发现另一种在Python中使用c结构的方法是使用PyCapsule。对于对此感兴趣的人,我找到了[link](http://chimera.labs.oreilly.com/books/1230000000393/ch15.html#capsules)。 – user1520280

+0

或者我认为你可以使用Cython,这是一款非常棒的软件。 – lbolla

+0

我准备好使用来自同事的c代码,我只是想处理python中数据的艰难预处理。但我应该花时间看看cython!谢谢。 – user1520280