2013-07-05 160 views
4

我有一个我想用Python访问的自制C库。问题在于代码基本上由两部分组成,一部分是从多个文件读入数据的初始化,另一部分是只需要执行一次的计算。另一部分在循环中调用,并使用重复前生成的数据。为了这个功能,我想从python传递参数。我的想法是编写两个C包装函数,“init”和“loop” - “init”读取数据并返回一个void指针,指向一个“loop”可以与其他参数一起使用的结构从python开始。像Python ctype - 如何在C函数之间传递数据

void *init() { 
    struct *mystruct ret = (mystruct *)malloc(sizeof(mystruct)); 
    /* Fill ret with data */ 
    return ret; 
} 

float loop(void *data, float par1, float par2) { 
    /* do stuff with data, par1, par2, return result */ 
} 

东西,我试过用Python调用“初始化”作为c_void_p,但由于“循环”改变一些“数据”和ctypes的空指针是不可改变的,这没有工作的内容。

我看到类似问题的其他解决方案似乎需要知道多少内存“初始化”将使用,我不知道。

有没有办法通过python将数据从一个C函数传递到另一个,而无需精确地告诉python究竟是什么或多少?或者有另一种方法来解决我的问题?


我试过(也失败了)编写了一个最小崩溃的例子,经过一些调试后发现我的C代码中有一个错误。感谢所有回复的人! 希望这可以帮助其他人,这里是一个排序最小的-工作版本(仍然没有独立的 '自由' - 对不起):

pybug.c:

#include <stdio.h> 
#include <stdlib.h> 

typedef struct inner_struct_s { 
    int length; 
    float *array; 
} inner_struct_t; 

typedef struct mystruct_S { 
    int id; 
    float start; 
    float end; 
    inner_struct_t *inner; 
} mystruct_t; 

void init(void **data) { 
    int i; 
    mystruct_t *mystruct = (mystruct_t *)malloc(sizeof(mystruct_t)); 
    inner_struct_t *inner = (inner_struct_t *)malloc(sizeof(inner_struct_t)); 
    inner->length = 10; 
    inner->array = calloc(inner->length, sizeof(float)); 
    for (i=0; i<inner->length; i++) 
    inner->array[i] = 2*i; 
    mystruct->id = 0; 
    mystruct->start = 0; 
    mystruct->end = inner->length; 
    mystruct->inner = inner; 
    *data = mystruct; 
} 

float loop(void *data, float par1, float par2, int newsize) { 
    mystruct_t *str = data; 
    inner_struct_t *inner = str->inner; 
    int i; 
    inner->length = newsize; 
    inner->array = realloc(inner->array, newsize * sizeof(float)); 
    for (i=0; i<inner->length; i++) 
    inner->array[i] = par1 + i * par2; 
    return inner->array[inner->length-1]; 
} 

编译为

在python
cc -c -fPIC pybug.c 
cc -shared -o libbug.so pybug.o 

运行:

from ctypes import * 
sl = CDLL('libbug.so') 
# What arguments do functions take/return? 
sl.init.argtype = c_void_p 
sl.loop.restype = c_float 
sl.loop.argtypes = [c_void_p, c_float, c_float, c_int] 

# Init takes a pointer to a pointer 
px = c_void_p() 
sl.init(byref(px)) 

# Call the loop a couple of times 
for i in range(10): 
    print sl.loop(px, i, 5, 10*i+5) 

回答

1

是一个DLL C代码?如果可以的话,可以考虑在那里创建一个全局指针。 init()将执行所需的初始化,并将指针设置为等于新分配的内存,并且loop()将在该内存上运行。另外不要忘记用close()函数释放它。

+0

这是一个linux .so共享库。我添加了一个全局空指针,将“ret”设置为指针,然后在“循环”中访问它,但仍然出现崩溃,看起来循环看不到“init”的结果。我会试着找出那里发生的事情。谢谢你的帮助! – Alex

+0

就这样我清楚...是“ret”全局指针吗?如果不是你想要设置“global_ptr”=(mystruct *)malloc(...)。然后在循环中使用global_ptr。如果这就是你在做什么,你仍然有崩溃,我会看看它更深 – sedavidw

2

当调用者完成时,你应该有一个相应的函数free数据缓冲区。否则,我不认为这个问题。只需将指针指向loop即可从init获得。

init.restype = c_void_p 
loop.argtypes = [c_void_p, c_float, c_float] 
loop.restype = c_float 

我不知道你所说的“ctypes的空指针是不可变”的意思,除非你在谈论c_char_pc_wchar_p。如果你传递一个Python字符串作为参数,它使用Python的私有指针指向字符串缓冲区。如果函数可以更改字符串,则应先将其复制到c_charc_wchar阵列。

下面是一个简单的例子,显示了将Python字符串(2.x字节字符串)作为参数传递给修改它的函数的问题。在这种情况下,它将索引0更改为'\ x00':

>>> import os 
>>> from ctypes import * 
>>> open('tmp.c', 'w').write("void f(char *s) {s[0] = 0;}") 
>>> os.system('gcc -shared -fPIC -o tmp.so tmp.c') 
0 
>>> tmp = CDLL('./tmp.so') 
>>> tmp.f.argtypes = [c_void_p] 
>>> tmp.f.restype = None 
>>> tmp.f('a') 
>>> 'a' 
'\x00' 
>>> s = 'abc' 
>>> tmp.f(s) 
>>> s 
'\x00bc' 

这特定于传递Python字符串作为参数。传递指向可变数据结构的指针不是问题,可以是ctypes数据对象(如Structure),也可以是库返回的指针。

+0

对不起,迟到的答复,并感谢您的答复。我指的是[链接](http://docs.python.org/2/library/ctypes.html#fundamental-data-types),它描述了关于c_char_p,c_wchar_p和c_void_p的信息:“然而,你应该小心,不要将它们传递给期望指向可变内存的函数。“这听起来像我不能改变指针指向的结构的内容。实际上,当它试图改变“数据”的内容时,代码崩溃。 – Alex

+0

我认为这就是你所指的,但这不是一般的指针;只是指向Python字符串使用的字符数组。 'data'只是在调用堆栈上推送的一个地址(一个整数)。你从'malloc(sizeof(mystruct))'得到地址。当然,你可以修改这些内存,所以如果你遇到了错误,那么你的代码特别错误。请用一个小例子更新你的问题。 – eryksun