Python中SWIG C ++类型的数组分配

时间:2011-05-02 21:54:16

标签: c++ python swig ctypes

我正在为使用SWIG公开其C ++ API的程序编写python脚本。 SWIG公开的函数有如下界面:

void writePixelsRect(JoxColor* colors, int left, int top, int width, int height);

JoxColor是一个POD结构,如下所示:

struct JoxColor {
    float r, g, b, a;
};

我可以在Python中轻松创建一个JoxColor并调用writePixelsRect调用,如下所示:

c = JoxApi.JoxColor()
c.r = r
c.g = g
c.b = b
c.a = a
JoxApi.writePixelsRect(c, x, y, 1, 1)

反复调用带有1x1像素矩形的writePixelsRect非常慢,所以我想从python创建一个JoxColor数组,这样我就可以编写更大的矩形了。 SWIG类型可以实现吗?

请注意,我无法访问暴露JoxColor和writePixelsRect的C ++库的源代码,因此无法为此添加帮助功能。我也不想在系统中引入新的C ++代码,因为它会强制我的python脚本的用户在他们运行的任何平台上编译C ++代码。我可以在python环境中访问ctypes,所以如果我能以某种方式将在ctypes中创建的float数组类型转换为适用于SWIG的JoxColor *类型,那么它对我有用。

2 个答案:

答案 0 :(得分:2)

这有点棘手,但至少对于这部分代码,您是否可以使用纯ctypes解决方案?基本上手动查看共享库文件导出的符号,以查找writePixelsRect函数导出的名称。 C ++确实命名为mangling,因为如果库作者选择使它writePixelsRect,它可能只是extern "C",它可能会更加混乱,比如_Z15writePixelsRectP8JoxColoriiii(这就是它的导出方式)我刚刚在我的系统上创建的虚拟C ++库。

在Linux上,此命令应该告诉您符号名称:

nm libjox.so | grep writePixel | cut -d " " -f 3

然后,保存该字符串并将其插入到Python代码中,如下所示:

from ctypes import *

LIBRARY_NAME = 'libjox.so'
c = cdll.LoadLibrary(LIBRARY_NAME)

WIDTH = 20
HEIGHT = 20

class JoxColor(Structure):
    _fields_ = [("r", c_float), ("g", c_float), ("b", c_float), ("a", c_float)]

ColorBlock = JoxColor * (WIDTH * HEIGHT)

data_array = ColorBlock()

color = JoxColor(0, 0, 1, 0)
for i in range(WIDTH * HEIGHT):
    data_array[i] = color

c._Z15writePixelsRectP8JoxColoriiii(data_array, 0, 0, WIDTH, HEIGHT)

假设_Z15writePixelsRectP8JoxColoriiii是可以在共享库中访问该函数的符号。运行此代码只是在我的系统上使用虚拟库,如下所示:

#include <stdio.h>

struct JoxColor {
    float r, g, b, a;
};

void writePixelsRect(JoxColor *colors, int left, int top, int width, int height) {
    int p = 0;
    printf("size: %i, %i\n", width, height);
    for (int i = 0; i < width; i++) {
        for (int j = 0; j < height; j++) {
            JoxColor color = colors[p];
            printf("pixel: %f, %f, %f, %f\n", color.r, color.g, color.b, color.a);
        }
    }
}

所以我希望它与您环境中的代码相差不远。

答案 1 :(得分:0)

禁止使用特殊的类型图,这个SWIG原型

void writePixelsRect(JoxColor* colors, int left, int top, int width, int height);

表示colorsJoxColor类型的单个对象,而不是数组。只用一个对象进行调用(尽管速度很慢)这一事实表明这是正确的。因此,传递数组可能只是从SWIG包装器代码中给出类型不匹配错误。

但老实说,这看起来像是一个写一个任意大矩形的函数。因此,如果你想绘制一个更大的矩形(一种颜色),只需要传递更大的宽度和/或高度:

JoxApi.writePixelsRect(c, x, y, 10, 20)

修改 我没有意识到你正在编写SWIG封装器,我认为这是提供给你的。在这种情况下,您可以编写一个类型映射,将Python列表(或元组,或任何您想要的)转换为JoxColor *。 SWIG文档展示了如何将Python字符串列表变为char **的示例:http://www.swig.org/Doc1.3/Python.html#Python_nn59 typemap使用Python C API进行转换,您可以使用Python文档所说的任何内容。基本上你分配一个JoxColor数组然后迭代Python列表对象并使用PyList_GetItem来获取每个单独的对象。这将返回SWIG包装的PyObject,您可以使用SWIG_ConvertPtr(list_item_py_object, (void**)&joxcolor_ptr, $descriptor(JoxColor *), 0)将其转换为指向实际JoxColor元素的指针。然后你可以将它复制到你的数组中。

请注意,JoxColor*的类型地图将适用于所有JoxColor*出现的地方,您可以说JoxColor* colors专门针对这种情况。

FYI,默认情况下,SWIG以完全相同的方式包装JoxColor *,JoxColor&amp;,JoxColor和JoxColor []作为单个对象。 Python只有对象,它不知道指针/引用/数组(Python列表也是对象)。 http://www.swig.org/Doc1.3/Python.html#Python_nn22