从Python中分配SWIG c++类型的数组

Array allocation of SWIG C++ type from Python

本文关键字:类型 数组 c++ SWIG Python 分配      更新时间:2023-10-16

我正在为一个程序编写一个python脚本,该程序使用SWIG公开了其c++ API。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中创建的浮点数组类型转换为SWIG的JoxColor*类型,它将为我工作。

这有点棘手,但是您是否可以,至少对于这部分代码,使用纯ctypes解决方案?基本上,手动查看共享库文件导出的符号,以查找writePixelsRect函数导出的名称。c++确实有名称混淆,所以如果库作者选择让它成为extern "C",它可能只是writePixelsRect,它可能是更混乱的东西,比如_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, %in", 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, %fn", color.r, color.g, color.b, color.a);
        }
    }
}

所以我希望它不会离你的环境中的工作代码太远。

除了特殊的类型映射,这个SWIG原型

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

表示colorsJoxColor类型的单个对象,而不是数组。您对单个对象的调用工作(尽管很慢)的事实表明这是正确的。因此,传递一个数组很可能只会给您一个来自SWIG包装器代码的类型不匹配错误。

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

JoxApi.writePixelsRect(c, x, y, 10, 20)
编辑:

我没有意识到您正在编写SWIG包装器,我以为已经提供给您了。在这种情况下,您可以编写一个typemap,将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*的typemap将适用于JoxColor*出现的任何地方,您可以使用JoxColor* colors来专门针对这种情况。

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