Python ctype - 如何在C函数之间传递数据

时间:2013-07-05 16:28:57

标签: python ctypes

我有一个我想用python访问的自制C库。问题在于代码基本上由两部分组成,一个是从多个文件中读取数据的初始化,另一个是只需要进行一次的计算。另一部分在循环中调用,并使用重复之前生成的数据。对于这个函数,我想从python传递参数。

我的想法是编写两个C包装函数,“init”和“loop” - “init”读取数据并返回一个void指针,指向一个“循环”可以与我可以传递的其他参数一起使用的结构来自python。像

这样的东西
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中调用“init”作为c_void_p,但由于“loop”改变了“data”的一些内容,而ctypes的void指针是不可变的,所以这不起作用。

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

有没有办法通过python将数据从一个C函数传递到另一个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];
}

编译为

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

在python中运行:

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)

2 个答案:

答案 0 :(得分:2)

调用者完​​成后,您应该有free数据缓冲区的相应功能。否则我没有看到这个问题。只需将指针传递给loop的{​​{1}}即可。

init

我不确定你的意思是“ctypes'无效指针是不可变的”,除非你在谈论init.restype = c_void_p loop.argtypes = [c_void_p, c_float, c_float] loop.restype = c_float c_char_p。问题在于,如果您将Python字符串作为参数传递,则它使用Python的字符串缓冲区的私有指针。如果函数可以更改字符串,则应首先将其复制到c_wchar_pc_char数组。

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

c_wchar

这特定于将Python字符串作为参数传递。将指针传递给旨在变为可变的数据结构既不是问题,也不是ctypes数据对象(如>>> 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' )或库返回的指针。

答案 1 :(得分:1)

您的C代码是否在DLL中?如果是这样可以考虑在那里创建一个全局指针。 init()将执行任何所需的初始化,并将指针设置为等于新分配的内存,loop()将对该内存进行操作。另外,不要忘记使用close()函数释放它