如何将使用python代码获得的str值(包含3000 {‘0′,’1’}字节)作为参数传递给python c扩展函数(使用SWIG扩展),这需要int *(固定长度int数组)as输入参数?我的代码是这样的:
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)()
ldpc.exposekey(arr)
3072 {0,1}进入该位置. Python返回语法错误:超过255个参数.这仍然无法帮助我传递指定的str值而不是初始化的ctypes int数组.
其他建议包括使用SWIG字体图,但是如何将str转换为int *?提前致谢.
解决方法:
关于我的评论,这里有一些关于从函数返回数组的更多细节:[SO]: Returning an array using C.简而言之:方法处理:
>将返回的变量设为静态
>动态分配它(使用malloc(family)或new)
>将其转换为该函数的附加参数
可以通过两种方式获得在Python解释器中运行的那段C代码:
既然他们都在做同样的事情,将它们混合在一起毫无意义.因此,选择最适合您需求的产品.
ctypes
>这就是你的开始
>这是使用ctypes做事的方法之一
ctypes_demo.c:
#include
#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位被忽略,而只使用其中一个;可以修复,但需要按位处理)
>我拿了一个并转向第二个参数(bitsOut).我认为这是最好的,因为分配和取消分配数组是调用者的责任(从头开始的第3个选项)
>我还修改了索引范围(没有更改功能),因为使用低索引值并在一个地方添加内容更有意义,而不是高索引值并在另一个地方减去(相同)某些内容
>返回值是设置的位数(显然,在这种情况下为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,我想提一下,运行代码并没有引发你得到的错误
>指定函数的argtypes和restype是必需的,也使事情变得更容易(在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):
06002
swig
>几乎所有来自ctypes部分的内容也适用于此处
swig_demo.c:
#include
#include
#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);
笔记:
>这里我分配数组并返回它(从头开始的第二个选项)
> .i文件是标准的swig接口文件
>定义模块及其导出(通过%include)
>值得一提的是%newobject指令,它释放由expose键返回的指针,以避免内存泄漏
> .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相同的技术
输出:
06007
3.普通的Python C API
>我将这部分作为个人练习加入
>这就是swig所做的,但是“手动”
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类似,但有一个(显而易见的)区别:来自swig_demo的import expose键应替换为capi_demo import exposekey
>输出也与test_swig.py相同(同样,这里不会复制它)
标签:python,arrays,string,int,swig