代码之家  ›  专栏  ›  技术社区  ›  4am

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

  •  0
  • 4am  · 技术社区  · 7 年前

    str int * (固定长度int数组)作为输入参数?我的代码是这样的:

    int *exposekey(int *bits) {
        int a[1000];
        for (int j=2000; j < 3000; j++) {
            a[j - 2000] = bits[j];
        }
        return a;
    }
    

    我试着用 ctypes (见以下代码):

    import ctypes
    ldpc = ctypes.cdll.LoadLibrary('./_ldpc.so')
    arr = (ctypes.c_int * 3072)(<mentioned below>)
    ldpc.exposekey(arr)
    

    3072{0,1}进入该位置。Python返回语法错误:超过255个参数。这仍然不能帮助我传递指定的str值,而不是初始化的str值 C类型 int数组。

    其他建议包括使用SWIG类型映射,但如何将str转换为 整数*

    1 回复  |  直到 7 年前
        1
  •  1
  •   CristiFati    5 年前

    关于我的评论,以下是有关从函数返回数组的更多详细信息: [SO]: Returning an array using C . 简而言之:处理方法:

    1. 生成返回的变量 静止的
    2. 马洛克 (家庭)或
    3. 将其转换为函数的附加参数

    得到那块 C 要在中运行的代码 蟒蛇 口译员有两种可能:

    因为他们都在做同一件事,把他们混在一起是没有意义的。所以,选择一个最适合你需要的。


    C类型

    • 这是你开始的
    • 它是 使用 C类型

    ctypes\u演示。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% 效率低下,因为每个字符的7位被忽略,而只使用其中一位;这可以修复,但需要 按位 处理)
    • 我拿走了 然后变成了2 nd公司 参数( 比特苏特 ). 我认为这是最好的,因为调用方负责分配和取消分配阵列(3 rd 选项)
    • 我还修改了索引范围(没有更改功能),因为处理低索引值并在一个位置向其添加一些内容更有意义,而不是在高索引值并在另一个位置减去(相同)一些内容
    • 返回值是设置的位数(显然,在本例中为1000),但这只是一个示例
    • 打印F 这只是一个假人,以表明 C
    • 在处理此类数组时,建议也传递其维数,以避免越界错误。而且 错误处理 是一个重要方面

    测试类型。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.
    • 指定函数的 argtypes 重新键入 C类型 教程)
    • 我正在打印 bits\u输出 数组(与其他部分一样,仅第一部分和相关部分) 0 )为了证明 C 代码完成了它的工作
    • bits\U in 2000虚拟阵列 0 bits\U字符串 )不是3000个字符长(出于明显的原因)。如果你的 bits\U字符串 3000个字符长,您可以简单地初始化 bits\U in 例如: bits_in = create_string_buffer(bits_string.encode())
    • 不要忘记 初始化 bits\u输出 对于大小足够大(在我们的示例中为1000)的阵列,否则 segfault 当试图将其内容设置为超过大小时可能会出现
    • 对于此(简单)函数 C类型 变体更容易(至少对我来说,因为我不使用 swig公司 通常情况下),但对于更复杂的功能/项目,这将成为一种过度使用,并切换到 swig公司 是正确的选择

    输出 (与一起运行 蟒蛇3.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. swig公司

    • C类型 第节也适用于此处

    swig_演示。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_演示。一、 :

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

    swig_演示。h :

    char *exposekey(char *bitsIn);
    

    :

    • 在这里,我分配数组并返回它(2 nd公司 选项)
    • 这个 .一、 文件是标准 swig公司 接口文件
      • %include
      • 值得一提的是 %newobject 指令,该指令取消分配由返回的指针 exposekey公司 避免内存泄漏
    • 这个 .h 文件只包含函数声明,以便包含在 .一、 文件(这不是强制性的,但这样做更优雅)

    测试开关。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()
    

    笔记 :

    • 事情变得更有意义 蟒蛇 程序员的 PoV
    • 代码要短得多(这是因为 swig公司 在幕后做了一些“魔术”:
      • 包装纸 .c .一、 文件已 约12万
      • 这个 swig_演示。py公司 生成的模块具有 约3K
    • 我对2000使用了相同的技术

    输出 :

    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公司 但“手动”

    capi_演示。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_演示。h (此处不复制其内容)
    • Python 3 (事实上,我很头疼,尤其是因为我习惯了 不再存在)
    • 错误处理较差
    • 测试capi。py公司 类似于 测试开关。py公司 有一个(明显的)区别: from swig_demo import exposekey 应替换为 from capi_demo import exposekey
    • 输出也与 测试开关。py公司 (再说一次,这里不重复)