【问题标题】:SWIG: mutable string typemap help neededSWIG:需要可变字符串类型映射帮助
【发布时间】:2016-03-04 21:40:09
【问题描述】:

这(我希望)是一个非常简单的问题,但尽管做了一些阅读(我是 SWIG 的新手,并且相当绿色 C-wise)我只是无法在我的头。

我有一个库中的函数(遗留代码,不想编辑):

extern int myfunction(char *infile, char *maskfile, int check, float *median, char *msg)

我的目标是使用 SWIG 在 Python 中为此创建一个包装器。

medianmsg 变量的值由 C 函数更改。当返回int != 0 时,msg arg 中会出现一些错误信息。如果返回int == 0,则median 变量将包含一个浮点数,其值是从myfunction 分配的。

这通常运行良好,返回值为 0。我使用 %array_functions%pointer_functions 创建需要传递的指针,根据这个 .i 文件:

%module test
%include "cpointer.i"
%include "carrays.i"
%{
 #include <stdint.h>
%}

extern int myfunction(char *infile, char *maskfile, int check, float *median, char *msg)

%pointer_functions(float, floatp);
%pointer_functions(char, charp);
%array_functions(char, charArray);

在swig-ing、编译和链接之后,我可以调用python中的函数了:

import test
errmsg_buffer = 1024
_infile = 'test2.dat'
infile  = imstat.new_charArray(len(_infile))
for i in xrange(len(_infile)):
    imstat.charArray_setitem(infile,i,_infile[i])
maskfile = imstat.new_charArray(1)
imstat.charArray_setitem(maskfile,0,'')
check = 0
med = imstat.new_floatp()
errmsg = imstat.new_charArray(errmsg_buffer)

out = test.myfunction(infile,maskfile,check,med,errmsg)
median = test.floatp_value(med)

这有时有效,但通常无效 - 我得到了很多段错误,这些错误通常通过更改 errmsg_buffer 长度来修复(显然不是一个有用的修复!)。更改msg 字符串的C 代码是:

(void)sprintf(errmsg,"file not found");

我的主要问题是正确处理msg 字符串,我怀疑这会导致段错误(并且可能是由于通过new_charArray 执行不正确?)。

最好的方法是什么?

我可以在.i 中添加一些东西,将char *msg 转换为python str吗?

这可以在不使用new_CharArray“预初始化”的情况下完成吗?如果errmsg_buffer 太小,我可能会遇到缓冲区溢出。

我希望这很清楚 - 很高兴添加 cmets 以供进一步讨论。

【问题讨论】:

    标签: python c swig


    【解决方案1】:

    使用 SWIG 可以大大简化您的包装器。试试这个 SWIG 接口文件(详情如下):

    %module test
    %include "typemaps.i"
    %include "cstring.i"
    %apply float *OUTPUT { float *median };
    %cstring_bounded_output(char *msg, 1024);
    extern int myfunction(char *infile, char *maskfile, int check, float *median, char *msg);
    

    然后,从 python 中,按以下方式使用模块:

    import test
    infile = 'test2.dat'
    maskfile = ''
    check = 0
    out, median, errmsg = test.myfunction(infile,maskfile,check)
    if out == 0: print(errmsg)
    ...
    

    但是,从你写的内容来看,我不太清楚你的方法为什么会出现段错误。

    详情

    • typemaps.i 文件包含 float *OUTPUT 类型映射,然后将其应用于 float *median 参数并将其从参数转换为 float 输出值。有关详细信息,请参阅argument handling 上的 SWIG 文档。

    • cstrings.i 文件包含处理 C 字符串的 SWIG 宏。在这里,我使用了%cstring_bounded_output 宏。这将创建一个给定大小 1024 的 char * 缓冲区,并自动将其作为 char *msg 的参数传递。然后,函数完成后的内容被转换为 python 字符串并附加到输出。详情请见here

    • SWIG 默认处理前两个char * 参数,即将python 字符串转换为适当的char * 并传递它们。请注意,为这些参数传递的 char * 是不可变的,即,如果您的 myfunction 尝试修改这些参数,将会发生不好的事情。了解 SWIG 如何处理 C 字符串 here

    • 因此,您包装的 myfunction 然后如上所示使用,并且在 python 中具有以下签名: myfunction(infile, maskfile, check) -&gt; (out, median, msg)

    编辑:

    关于 carrays.i 状态的 SWIG 文档:

    注意: %array_functions()%array_class() 不应与 charchar * 类型一起使用。

    我认为您的代码没有正确创建以 NULL 结尾的 C char *,因此这可能会导致段错误。

    【讨论】:

    • 谢谢@m7thon。这是一个非常非常好的答案:清楚地解释,在原始代码的上下文中提供了一个布局良好的解决方案。关于我怀疑的段错误的最后一点也是正确的。一些快速查询要跟进: - 是正确的返回顺序 然后是接口文件中声明的顺序吗? - 我为使用 PyString_AsString($input) 的char *infile 添加了一个%typemap(in)。如果我“故意”从 python 传递int,它会出现段错误 - 我怎样才能让 SWIG 更好地处理这个问题?它可以进行类型检查并抛出异常吗?
    • 返回顺序确定如下:首先,$result(SWIG 内部变量)只是转换后的(到 python)。然后,转换为输出的参数(通过“argout 类型映射”)按照参数在函数中出现的顺序进行处理,并且通常附加$result。所以如果你想让medianmsg成为输出,顺序一般是(return_val, median, msg)。话虽如此,您可以编写自己的 argout 类型映射,将其 prepend 而不是 append$result,但 SWIG 提供的 argout 类型映射总是附加。
    • 如果您编写自己的“in”类型映射,您会收到PyObject*。您需要检查它的类型是否正确(PyString_Check)并相应地处理错误(使用例如SWIG_exception(SWIG_RuntimeError, "error message");)。但是转换 python 字符串很棘手,因为您还需要担心编码(字节、unicode)。让 SWIG 为您完成工作。看看SWIG's typemap codeSWIG's conversion code
    【解决方案2】:

    我对 SWIG 的学习不是很深。但我试着给你一些建议。 1. 如果您的程序修改了输入参数或使用它返回数据,请考虑使用 SWIG 库章节中描述的 cstring.i 库文件。 数据被复制到一个新的 Python 字符串中并返回。 如果您的程序需要处理二进制数据,您可以使用类型映射将 Python 字符串扩展为指针/长度参数对。幸运的是,已经定义了这样的类型映射。只需这样做:

        %apply (char *STRING, int LENGTH) { (char *data, int size) };
       ...
       int parity(char *data, int size, int initial);
    

    Python:

    parity("e\x09ffss\x00\x00\x01\nx", 0)
    

    如果您需要返回二进制数据,您可以使用 cstring.i 库文件。 cdata.i 库也可用于从任意指针中提取二进制数据。

    2.我认为“预初始化”可能是必要的。

    【讨论】:

      猜你喜欢
      • 2010-11-18
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      • 1970-01-01
      • 1970-01-01
      • 2016-11-26
      • 2011-01-06
      • 1970-01-01
      相关资源
      最近更新 更多