简体   繁体   English

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

[英]Store file contents in a buffer and assign it to a pointer in Python

I'm using SWIG to link some C libraries I wrote to Python. 我正在使用SWIG将我编写的一些C库链接到Python。 One of my C functions takes a struct as a parameter, and one of its data fields is a pointer (uint32_t *data). 我的C函数之一将struct作为参数,其数据字段之一是指针(uint32_t * data)。 Now I need to read in a file in Python and pass the contents to that pointer. 现在,我需要使用Python读取文件,并将内容传递给该指针。 I did it like this: 我这样做是这样的:

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

But it complains type mismatch when I run it. 但是当我运行它时,它抱怨类型不匹配。 I'd appreciate any help, thanks in advance. 非常感谢您的帮助,在此先感谢您。

As an example let's work with the following header file (lib.h): 作为示例,让我们使用以下头文件(lib.h):

#include <stdint.h>

struct foo {
  const uint32_t *data;
};

void bar(struct foo *f);

and a corresponding implementation (lib.c): 和相应的实现(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]);
  }
}

I compiled this with: 我用以下代码编译了它:

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

and generated some test data in a file (input.dat) with: 并使用以下命令在文件(input.dat)中生成了一些测试数据:

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

this assumes that the data you're planing to read is already in machine endian form in your file. 这假设您打算读取的数据已经在文件中为机器字节序形式。

Passing the contents of input.dat from Python to bar via the foo.data can be done with both ctypes and SWIG. 可以使用ctypes和SWIG都将foo.data的input.dat内容从Python传递到bar

Ctypes C类型

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

The main work here is the call to ctypes.cast with the string buffer created from what we read in from the file that allows the assignment to happen (correctly). 此处的主要工作是使用从我们从文件中读取的内容创建的字符串缓冲区调用ctypes.cast ,该文件允许分配(正确)发生。

(I think the call to ctypes.byref(arg) when calling bar is redundant also because we already set argtypes for bar). (我认为在调用bar时对ctypes.byref(arg)的调用也是多余的,因为我们已经为bar设置了argtypes)。

SWIG 斯威格

For SWIG things are a little different and not the simplest SWIG usage. 对于SWIG,情况有所不同,而不是最简单的SWIG用法。 This is because of the low-level casting we need to do. 这是因为我们需要做的底层转换。 (Normally you'd do %array_class and build the array in Python, or simply use a Python list instead). (通常,您可以执行%array_class并使用Python构建数组,或者直接使用Python列表)。

This is the module interface I used for a SWIG equivalent: 这是我用于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
}
  1. Tell SWIG to ignore foo::data when it see it inside the headerfile - we want to replace it with something that can be used as a String 告诉SWIG在头文件中看到foo::data时忽略它-我们想用可以用作String的内容替换它
  2. After reading lib.h clear the %ignore so we can %extend and add our alternative version 阅读lib.h之后,清除%ignore,以便我们可以%extend并添加替代版本
  3. C code only implementation of getters and setters for our special version of foo::data . 我们的特殊版本foo::data C语言仅实现getter和setter的实现。 All the hardwork is actually done behind the scenes for us by he SWIG libraries. 实际上,所有辛勤工作都是由SWIG库在后台为我们完成的。
  4. Tell SWIG that we'd like to pretend that foo::data is a const char * instead. 告诉SWIG我们要假装foo::dataconst char *

We build it with: 我们用以下方法构建它:

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

This lets us write something like: 这使我们可以编写如下内容:

from method2 import *

# Call the function with the argument:

arg = foo()

with open('input.dat', 'rb') as f:
  arg.data = f.read()

bar(arg)

Which is simple and works exactly as you'd hope. 这很简单,并且完全符合您的期望。 (Note: this only works with Python 2.7, a Python 3 solution is slightly more complex since f.read() is bytes not str) (注意:这仅适用于Python 2.7,Python 3解决方案稍微复杂一些,因为f.read()是字节而不是str)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM