将文件内容存储在缓冲区中,并将其分配给Python中的指针

时间:2014-11-12 18:17:16

标签: python c pointers swig ctypes

我使用SWIG将我写的一些C库链接到Python。我的一个C函数将struct作为参数,其中一个数据字段是一个指针(uint32_t * data)。现在我需要读取Python中的文件并将内容传递给该指针。我是这样做的:

p = struct_t() # create the C struct 
p.data = open(fp, 'r').read() # let the pointer point to the buffer
feria_op(p) # call the C routine

但是当我运行它时,它会抱怨类型不匹配。我提前感谢,感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

作为示例,让我们使用以下头文件(lib.h):

#include <stdint.h>

struct foo {
  const uint32_t *data;
};

void bar(struct foo *f);

和相应的实现(lib.c):

#include "lib.h"
#include <stdio.h>
#include <assert.h>

void bar(struct foo *f) {
  assert(f);
  assert(f->data);
  for (unsigned i = 1; i <= f->data[0]; ++i) {
    fprintf(stdout, "%d\n", (int)f->data[i]);
  }
}

我编译了这个:

gcc -Wall -Wextra -shared -o lib.so lib.c -std=c99 -g

并在文件(input.dat)中生成一些测试数据:

with open('input.dat', 'wb') as f:
  f.write(struct.pack("IIII", 3,1,2,3))

这假定您计划阅读的数据已经在您的文件中以机器端格式存在。

可以使用ctypes和SWIG将input.dat的内容从Python传递到bar {/ 1}}。

ctypes的

foo.data

这里的主要工作是调用import ctypes lib = ctypes.CDLL("lib.so") class foo(ctypes.Structure): _fields_ = [('data', ctypes.POINTER(ctypes.c_uint32))] bar = lib.bar bar.argtypes = [ctypes.POINTER(foo)] # Call the function with the argument: arg = foo() with open('input.dat', 'rb') as f: arg.data = ctypes.cast(ctypes.create_string_buffer(f.read()), ctypes.POINTER(ctypes.c_uint32)) bar(ctypes.byref(arg)) ,其字符串缓冲区是根据我们从文件读入的内容创建的,该文件允许分配发生(正确)。

(我认为调用bar时调用ctypes.byref(arg)也是多余的,因为我们已经为bar设置了argtypes。)

SWIG

对于SWIG,事情有点不同,而不是最简单的SWIG用法。这是因为我们需要进行低级别的投射。 (通常你会做ctypes.cast并在Python中构建数组,或者只是使用Python列表。)

这是我用于SWIG等效项的模块接口:

%array_class
  1. 当它在头文件中看到它时,告诉SWIG忽略%module method2 %{ #include "lib.h" %} %ignore foo::data; // #1 %include "lib.h" %rename("%s") foo::data; // #2 // #3 %{ void foo_data_set(struct foo *f, char const *data) { f->data = data; } const char *foo_data_get(const struct foo *f) { return NULL; // Hard to make this meaningful } %} %extend foo { const char *data; // #4 } - 我们想用可以用作String的东西替换它
  2. 阅读lib.h后,清除%ignore,以便我们可以%扩展并添加我们的替代版本
  3. 仅针对我们特殊版本foo::data的C代码实现getter和setter。所有的辛勤工作实际上是由SWIG图书馆为我们幕后完成的。
  4. 告诉SWIG,我们想假装foo::data代替foo::data
  5. 我们用以下方式构建它:

    const char *

    这让我们可以这样写:

    swig -python -Wall method2.i && gcc -Wall -Wextra method2_wrap.c -I/usr/include/python2.7/ -lpython2.7 -shared -o _method2.so ./lib.so
    

    这很简单,完全按照您的希望运作。 (注意:这仅适用于Python 2.7,Python 3解决方案稍微复杂一些,因为from method2 import * # Call the function with the argument: arg = foo() with open('input.dat', 'rb') as f: arg.data = f.read() bar(arg) 是字节而不是str)