SWIG C++ 的数组分配从 Python 输入
我正在为一个程序编写一个 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* 类型,那么它对我来说会很有用。
I'm writing a python script for a program that has exposed its C++ API using SWIG.
A SWIG exposed function has an interface like this:
void writePixelsRect(JoxColor* colors, int left, int top, int width, int height);
JoxColor is a POD struct looking like this:
struct JoxColor {
float r, g, b, a;
};
I can easily create a single JoxColor in Python and invoke a call to writePixelsRect like this:
c = JoxApi.JoxColor()
c.r = r
c.g = g
c.b = b
c.a = a
JoxApi.writePixelsRect(c, x, y, 1, 1)
Repeatedly calling writePixelsRect with a 1x1 pixel rectangle is very slow so I want to create an array of JoxColor from python so I can write bigger rectangles at the time. Is this possible with SWIG types?
Note that I don't have access to the source code for the C++ library exposing JoxColor and writePixelsRect so I can't add a help function for this. I also don't want to introduce new C++ code in the system since it would force the users of my python script to compile the C++ code on whatever platform they are running. I do have access to ctypes in the python environment so if I could somehow typecast a float array created in ctypes to the type of JoxColor* for SWIG it would work for me.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这有点棘手,但是至少对于这部分代码,您可以使用纯 ctypes 解决方案吗?基本上手动查看共享库文件导出的符号以查找 writePixelsRect 函数导出的名称。 C++ 确实有名称修饰,因此,如果库作者选择将其设为
extern "C"
,它可能只是writePixelsRect
,但它可能会更加混乱,例如_Z15writePixelsRectP8JoxColoriiii
(这就是它在我刚刚在系统上创建的虚拟 C++ 库中导出的方式)。在 Linux 上,此命令应该告诉您符号名称:
然后,保存该字符串并将其插入到 Python 代码中,如下所示:
假设
_Z15writePixelsRectP8JoxColoriiii
是该函数可在共享库中访问的符号。运行此代码只是在我的系统上使用如下所示的虚拟库运行:所以我希望它与您环境中的工作代码相距不远。
This is kinda tricky, but could you, at least for this part of the code, use a pure-ctypes solution? Basically manually look at the symbols exported by the shared libary file to find the name that the writePixelsRect function was exported as. C++ does name mangling, so while it might just be
writePixelsRect
if the library authors chose to make itextern "C"
, it might be something much messier, like_Z15writePixelsRectP8JoxColoriiii
(that's how it was exported in a dummy C++ library I just created on my system).On Linux, this command should tell you the symbol name:
Then, save that string and insert it into Python code kinda like this:
Assuming that
_Z15writePixelsRectP8JoxColoriiii
is the symbol that the function is accessible as in the shared library. Running this code just worked on my system with a dummy library like this:So I'm hopeful that it's not too far from working code in your environment.
除非使用特殊的类型映射,此 SWIG 原型
意味着
colors
是JoxColor
类型的单个对象,而不是数组。事实上,您仅使用单个对象进行的调用就可以工作(尽管速度很慢),这表明这是事实。因此,传递数组可能只会给您带来 SWIG 包装器代码中的类型不匹配错误。但老实说,这看起来像是一个写入任意大矩形的函数。因此,如果您想绘制更大的矩形(一种颜色),只需传入更大的宽度和/或高度:
编辑:
我没有意识到您正在编写 SWIG 包装器,我以为这是提供给您的。在这种情况下,您可以编写一个类型映射,将 Python 列表(或元组,或任何您想要的)转换为 JoxColor*。
SWIG 文档显示了如何将 Python 字符串列表转换为 char** 的示例: http://www.swig.org/Doc1.3/Python.html#Python_nn59
类型映射使用 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* color
将其专门用于这种情况。仅供参考,默认情况下,SWIG 以完全相同的方式将 JoxColor*、JoxColor&、JoxColor 和 JoxColor[] 包装为单个对象。 Python 只有对象,它不知道指针/引用/数组(Python 列表也是对象)。 http://www.swig.org/Doc1.3/Python.html#Python_nn22
Barring special typemaps, this SWIG prototype
means that
colors
is a single object of typeJoxColor
, not an array. The fact that your call with just a single object works (albeit slowly) suggests that that's true. So passing an array is likely to just give you a type mismatch error from the SWIG wrapper code.But honestly, this looks like a function that writes an arbitrarily-large rectangle. So if you want to draw a larger rectangle (of one color) just pass in a larger width and/or height:
Edit:
I didn't realize you were writing the SWIG wrapper, I thought that was provided to you. In that case you can write a typemap that will convert a Python list (or tuple, or whatever you want) into JoxColor*.
The SWIG docs show an example for how turn a Python list-of-strings into char**: http://www.swig.org/Doc1.3/Python.html#Python_nn59
The typemap uses Python C APIs to do the conversion, you can use anything the Python docs say. Essentially you allocate a JoxColor array then iterate over the Python list object and use
PyList_GetItem
to get each individual object. That will return a SWIG-wrapped PyObject, you can useSWIG_ConvertPtr(list_item_py_object, (void**)&joxcolor_ptr, $descriptor(JoxColor *), 0)
to convert that into a pointer to your actual JoxColor element. Then you can copy that into your array.Remember that a typemap for
JoxColor*
will apply everywhereJoxColor*
appears, you can sayJoxColor* colors
to specialize it to just this case.FYI, by default SWIG wraps JoxColor*, JoxColor&, JoxColor, and JoxColor[] in exactly the same way, as a single object. Python only has objects, it doesn't know of pointers/references/arrays (Python lists are also objects). http://www.swig.org/Doc1.3/Python.html#Python_nn22