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

凌晨 4 点

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

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 值而不是初始化的ctypesint 数组。

其他建议包括使用 SWIG 类型映射,但是如何将 str 转换为int *提前致谢。

克里斯蒂·法蒂

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

  1. 使返回的变量静态
  2. 动态分配它(使用malloc (family) 或new
  3. 把它变成函数的附加参数

可以通过两种方式让那段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 倍(尽管由于每个字符的 7 位被忽略而效率仍然低到700%,而只使用其中之一;这可以修复,但是需要按位处理)
  • 我拿了a并变成了第二个参数(bitsOut)。我认为这是最好的,因为分配和释放数组是调用者的责任(从一开始的第三个选项)
  • 我还修改了索引范围(不改变功能),因为使用低索引值并在一个地方向它们添加一些东西更有意义,而不是高索引值并在另一个地方减去(相同)一些东西
  • 返回值是设置的位数(显然,在本例中为 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,我要提一提,运行代码没有提高,你得到了错误
  • 指定函数的argtypesrestype 强制性的,也使事情变得更容易(记录在ctypes教程中)
  • 我正在打印bits_out数组(只有第一部分和相关部分,其余部分都是0)以证明C代码完成了它的工作
  • 在开始时用 2000 虚拟0初始化bits_in数组,因为这些值在这里不相关。此外,输入字符串 ( bits_string ) 的长度不是 3000 个字符(原因很明显)。如果您的bits_string是 3000 个字符长,您可以简单地初始化bits_in如下:bits_in = create_string_buffer(bits_string.encode())
  • 不要忘了初始化bits_out与尺寸足够大的数组(在我们的例子1000),其目的,否则段错误试图设置其内容过去的大小时可能出现
  • 对于这个(简单的)函数,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);

注意事项

  • 在这里,我正在分配数组并返回它(从一开始的第二个选项)
  • 。我的文件是一个标准的痛饮接口文件
    • 定义模块及其导出(通过%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在幕后做了一些“魔法”):
    • .i文件生成的包装器.c包装器文件有~120K
    • 所述swig_demo.py生成模块具有〜3K
  • 在字符串的开头使用了与 2000 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所做的,但是“手动”

capo_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.hswig_demo.c(这里不会复制它们的内容)
  • 不仅可与Python 3中(其实我意识到自己有些头疼使其工作,特别是因为我是用来PyString_AsString它不再存在)
  • 错误处理很差
  • test_capi.pytest_swig.py相似,有一个(明显的)区别:from swig_demo import exposekey应替换为from capi_demo import exposekey
  • 输出也与test_swig.py相同(这里不再重复)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

使用SWIG将numpy数组元素(int)传递给c ++ int

将python函数传递给SWIG包装的C ++代码

使用numpy和swig将2d数组作为参数传递给函数

用C语言编写python扩展时,如何将python函数传递给C函数?

将结构数组传递给函数作为指针(C)

将String数组作为输入传递给外部C函数

将值直接作为数组传递给C函数

将数组作为参数传递给C函数

将指针传递给char数组作为函数的参数-C

SWIG:将2d numpy数组传递给C函数f(double a [])

使用Boost Python将C ++函数扩展到Python

使用Swig将numpy字符串数组传递给C

如何将Python中的字节对象传递给Swig包装的C ++函数?

SWIG:如何将复数向量从C ++传递给python

将数组作为参数传递给函数

扩展python-swig,而不是swig或Cython

如何使用Cython将python函数作为参数传递给c ++函数

当我尝试将字符串作为参数传递给Azure自定义脚本扩展中的Powershell函数时,“传递了无效的数组...”

SWIG C-to-Python Int数组

将UIAlertAction的函数传递给UIAlertController扩展

将方法扩展作为参数传递

如何将扩展React.Component的类作为属性传递给它(使用babel)?

无法为C ++ Python扩展编译Swig生成的包装器

如何在将C ++宏作为参数传递给另一个宏之前对其进行扩展

如何将整个numpy数组作为python中的参数传递给用户定义的函数?

将3D数组作为参数传递给C中的函数

有什么方法可以将未知类型的数组作为参数传递给C中的函数?

如何将char数组从C JNI函数作为字节传递给Java方法[]

将数组作为参数传递给 C 中的 x86 函数