2014-01-09 56 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