将str作为int数组传递给Python C扩展函数(使用SWIG扩展)

时间:2017-11-14 01:41:22

标签: python arrays string int swig

如何将使用python代码获取的str值(包含3000 {'0','1'}字节)作为参数传递给python c扩展函数(使用SWIG扩展),这需要{{ 1}}(固定长度的int数组)作为输入参数?我的代码是这样的:

int *

我尝试过使用int *exposekey(int *bits) { int a[1000]; for (int j=2000; j < 3000; j++) { a[j - 2000] = bits[j]; } return a; } (见下面的代码):

ctypes

以3072 {0,1}进入该位置。 Python返回语法错误:超过255个参数。这仍然无法帮助我传递指定的str值而不是初始化的import ctypes ldpc = ctypes.cdll.LoadLibrary('./_ldpc.so') arr = (ctypes.c_int * 3072)(<mentioned below>) ldpc.exposekey(arr) int数组。

其他建议包括使用SWIG字体图,但如何将str转换为ctypes?提前谢谢。

1 个答案:

答案 0 :(得分:1)

关于我的评论,这里有一些关于从函数返回数组的更多细节:[SO]: Returning an array using C。简而言之:处理这个问题的方法:

  1. 将返回的变量 静态
  2. 动态分配(使用 malloc (系列)或
  3. 将其转换为函数的附加参数
  4. C 代码在 Python 解释器中运行可以通过两种方式实现:

    既然他们都在做同样的事情,将它们混合在一起毫无意义。因此,选择最适合您需求的那个。


    1。 ctypes的

    • 这是你开始的
    • 使用 ctypes

    ctypes_demo.c

    #include <stdio.h>
    
    #if defined(_WIN32)
    #  define CTYPES_DEMO_EXPORT_API __declspec(dllexport)
    #else
    #  define CTYPES_DEMO_EXPORT_API
    #endif
    
    
    CTYPES_DEMO_EXPORT_API int exposekey(char *bitsIn, char *bitsOut) {
        int ret = 0;
        printf("Message from C code...\n");
        for (int j = 0; j < 1000; j++)
        {
            bitsOut[j] = bitsIn[j + 2000];
            ret++;
        }
        return ret;
    }
    

    备注

    • 根据评论,我将函数中的类型从int*更改为char*,因为它的紧凑性要高4倍(尽管它仍然 ~700 %效率低,因为每个char的7位被忽略,而只使用其中一个;可以修复,但需要按位处理)
    • 我拿了 a 并转入2 nd 参数( bitsOut )。我认为这是最好的,因为调用者负责分配和取消分配数组(从头开始的3 rd 选项)
    • 我还修改了索引范围(没有改变功能),因为使用低索引值并在一个地方添加一些内容更有意义,而不是高索引值并减去(另一个)另一个地方
    • 返回值是设置的位数(显然,在这种情况下为1000),但它只是一个例子
    • printf 它只是假的,表明 C 代码已被执行
    • 在处理此类数组时,建议也要传递它们的尺寸,以避免出现界限错误。此外,错误处理是一个重要方面

    test_ctypes.py

    from ctypes import CDLL, c_char, c_char_p, c_int, create_string_buffer
    
    
    bits_string = "010011000110101110101110101010010111011101101010101"
    
    
    def main():
        dll = CDLL("./ctypes_demo.dll")
        exposekey = dll.exposekey
    
        exposekey.argtypes = [c_char_p, c_char_p]
        exposekey.restype = c_int
    
        bits_in = create_string_buffer(b"\0" * 2000 + bits_string.encode())
        bits_out = create_string_buffer(1000)
        print("Before: [{}]".format(bits_out.raw[:len(bits_string)].decode()))
        ret = exposekey(bits_in, bits_out)
        print("After: [{}]".format(bits_out.raw[:len(bits_string)].decode()))
        print("Return code: {}".format(ret))
    
    
    if __name__ == "__main__":
        main()
    

    备注

    • 1 st ,我想提一下,运行代码并没有引起你的错误
    • 指定功能 argtypes restype 是强制性的,并且还可以使事情变得更轻松(在 ctypes中记录< / em> tutorial)
    • 我正在打印 bits_out 数组(只有第一个 - 相关部分,其余部分 0 )才能证明 C < / em>代码完成了它的工作
    • 我在开头使用2000 dummy 0 初始化 bits_in 数组,因为这些值与此处无关。此外,输入字符串( bits_string )长度不是3000个字符(原因很明显)。如果您的 bits_string 长度为3000个字符,则只需初始化 bits_in ,如:bits_in = create_string_buffer(bits_string.encode())
    • 不要忘记 bits_out 初始化为大小足够大的数组(在我们的示例中为1000),否则 segfault 在尝试将其内容设置为超出大小时可能会出现
    • 对于这个(简单)功能, ctypes 变体更容易(至少对我而言,因为我不经常使用 swig ),但更多复杂的功能/项目它将成为一种过度杀伤,并且切换到 swig 将是正确的做法

    输出(在 Win 上使用 Python3.5 运行):

    c:\Work\Dev\StackOverflow\q47276327>"c:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" test_ctypes.py
    Before: [                                                   ]
    Message from C code...
    After: [010011000110101110101110101010010111011101101010101]
    Return code: 1000
    


    2。 痛饮

    • 几乎来自 ctypes 部分的所有内容,也适用于此处

    swig_demo.c

    #include <malloc.h>
    #include <stdio.h>
    #include "swig_demo.h"
    
    
    char *exposekey(char *bitsIn) {
        char *bitsOut = (char*)malloc(sizeof(char) * 1000);
        printf("Message from C code...\n");
        for (int j = 0; j < 1000; j++) {
            bitsOut[j] = bitsIn[j + 2000];
        }
        return bitsOut;
    }
    

    swig_demo.i

    %module swig_demo
    %{
    #include "swig_demo.h"
    %}
    
    %newobject exposekey;
    %include "swig_demo.h"
    

    swig_demo.h

    char *exposekey(char *bitsIn);
    

    备注

    • 我在这里分配数组并返回它(从头开始的2 nd 选项)
    • .i 文件是标准的 swig 接口文件
      • 定义模块及其导出(通过%include
      • 值得一提的是%newobject指令解除了 exposekey 返回的指针以避免内存泄漏
    • .h 文件只包含函数声明,以便包含在 .i 文件中(它不是强制性的,但事情更多优雅的这种方式)
    • 其余几乎相同

    test_swig.py

    from swig_demo import exposekey
    
    bits_in = "010011000110101110101110101010010111011101101010101"
    
    
    def main():
        bits_out = exposekey("\0" * 2000 + bits_in)
        print("C function returned: [{}]".format(bits_out))
    
    
    if __name__ == "__main__":
        main()
    

    备注

    • Python 程序员 PoV
    • 开始,事情变得更有意义
    • 代码短得多(因为 swig 做了一些&#34;魔术&#34;在幕后):
      • .i 文件生成的包装器 .c 包装器文件 ~120K
      • swig_demo.py 生成的模块 ~3K
    • 我在字符串
    • 的开头使用了与 0 相同的技术

    <强>输出

    c:\Work\Dev\StackOverflow\q47276327>"c:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" test_swig.py
    Message from C code...
    C function returned: [010011000110101110101110101010010111011101101010101]
    


    3。普通的 Python C API

    • 我将此部分作为个人练习添加
    • 这就是 swig 的作用,但&#34;手动&#34;

    capi_demo.c

    #include "Python.h"
    #include "swig_demo.h"
    
    #define MOD_NAME "capi_demo"
    
    
    static PyObject *PyExposekey(PyObject *self, PyObject *args) {
        PyObject *bitsInArg = NULL, *bitsOutArg = NULL;
        char *bitsIn = NULL, *bitsOut = NULL;
        if (!PyArg_ParseTuple(args, "O", &bitsInArg))
            return NULL;
        bitsIn = PyBytes_AS_STRING(PyUnicode_AsEncodedString(bitsInArg, "ascii", "strict"));
        bitsOut = exposekey(bitsIn);
        bitsOutArg = PyUnicode_FromString(bitsOut);
        free(bitsOut);
        return bitsOutArg;
    }
    
    
    static PyMethodDef moduleMethods[] = {
        {"exposekey", (PyCFunction)PyExposekey, METH_VARARGS, NULL},
        {NULL}
    };
    
    
    static struct PyModuleDef moduleDef = {
        PyModuleDef_HEAD_INIT, MOD_NAME, NULL, -1, moduleMethods
    };
    
    
    PyMODINIT_FUNC PyInit_capi_demo(void) {
        return PyModule_Create(&moduleDef);
    }
    

    备注

    • 需要 swig_demo.h swig_demo.c (此处不会复制其内容)
    • Python 3 一起工作(实际上我有些头疼使它工作,特别是因为我习惯了 PyString_AsString 这是不再存在)
    • 错误处理不佳
    • test_capi.py test_swig.py 类似,但有一个(明显的)差异:from swig_demo import exposekey应替换为from capi_demo import exposekey
    • 输出也与 test_swig.py 相同(再次,不会在此处复制)