[更新:问题已解决!参见文章底部]
我需要允许Python开发人员将一组打包数据(在这种情况下)通过API插入到API中,这是通过Python C API手动暴露的一系列C++接口。我的初步印象是使用ctypes Structure类来实现这样的接口:class Vertex(Structure):
_fields_ = [
('x', c_float),
('y', c_float),
('z', c_float),
('u', c_float),
('v', c_float),
('color', c_int)
]
verts = (Vertex * 3)()
verts[0] = Vertex(0.0, 0.5, 0.0, 0.0, 0.5, 0xFF0000FF)
verts[1] = Vertex(0.5, -0.5, 0.0, 0.5, -0.5, 0x00FF00FF)
verts[2] = Vertex(-0.5, -0.5, 0.0, -0.5, -0.5, 0x0000FFFF)
device.ReadVertices(verts, 3) # This is the interfaces to the C++ object
其中我要传递给的函数具有以下签名:
^{pr2}$
Python包装器如下所示:static PyObject* Device_ReadVertices(Py_Device* self, PyObject* args)
{
PyObject* py_verts;
int count;
if(!PyArg_ParseTuple(args, "Oi", &py_verts, &count))
return NULL;
// This Doesn't Work!
Vertex* verts = static_cast(PyCObject_AsVoidPtr(py_verts));
self->device->ReadVertices(verts, count);
Py_RETURN_NONE;
}
当然,我遇到的最大问题是:我可以检索结构的PyObject,但我不知道如何将其转换为正确的类型。上面的代码失败得很惨。那么,我到底该如何允许用户从Python传递这种数据呢?在
现在,有两件事要考虑:首先,我已经有了相当多的Python/C++层,我对它非常满意(我远离SWIG,这样我可以有更多的灵活性)。我不想重新编写代码,所以我更喜欢一个与C API本机工作的解决方案。其次,我确实希望在我的C++代码中预先定义顶点结构,所以我不希望用户需要在Python中重新定义它(这样减少错误),但是我不知道如何暴露这样一个连续的结构。第三,我没有理由尝试ctypes结构,除了不知道另一种方法。欢迎提出任何建议。最后,由于这是一个图形应用程序(你可能已经猜到了),我更喜欢一个更快的方法,而不是一个方便的方法,即使更快的方法需要更多的工作。在
谢谢你的帮助!我仍然对python扩展有自己的看法,所以在一些比较棘手的部分获得社区的意见是很有帮助的。在
[解决方案]
首先,感谢每一个提出自己想法的人。最终的答案是由许多小道消息加起来的。最后,我发现了:山姆的使用建议结构包结果钱是对的。看到我使用的是Python 3,我不得不稍微调整一下,但是当我说了这么多之后,屏幕上出现了一个三角形:verts = bytes()
verts += struct.pack("fffffI", 0.0, 0.5, 0.0, 0.0, 0.5, 0xFF0000FF)
verts += struct.pack("fffffI", 0.5, -0.5, 0.0, 0.5, -0.5, 0x00FF00FF)
verts += struct.pack("fffffI", -0.5, -0.5, 0.0, -0.5, -0.5, 0x0000FFFF)
device.ReadVertices(verts, 3)
现在我的元组解析如下所示:static PyObject* Device_ReadVertices(Py_Device* self, PyObject* args)
{
void* py_verts;
int len, count;
if(!PyArg_ParseTuple(args, "y#i", &py_verts, &len, &count))
return NULL;
// Works now!
Vertex* verts = static_cast(py_verts);
self->device->ReadVertices(verts, count);
Py_RETURN_NONE;
}
请注意,即使我在本例中没有使用len变量(虽然在最终产品中我会使用),但我需要使用'y#'而不是仅使用'y'来解析元组,否则它将在第一个NULL处停止(根据文档)。同样要考虑的是:像这样的void*转换是相当危险的,所以请加载比我这里显示的更多的错误检查!在
所以,干得好,快乐的一天,打包回家,是吗?在
等等!别这么快!还有更多!在
对这一切的结果感觉很好,我突发奇想,决定看看我之前的尝试是否仍然失败,并回到本文中的第一个python片段。(当然,使用新的C代码)和。。。成功了!结果与结构包版本!真 的!在
所以这意味着您的用户可以选择如何提供这类数据,并且您的代码可以不做任何更改就处理这两种数据。我个人会鼓励类型结构方法,因为我认为它更容易阅读,但实际上它是任何用户满意的。(见鬼,如果他们愿意的话,他们可以手动键入一个十六进制的字节串。它起作用了。我试过了。)
老实说,我认为这是最好的结果,所以我欣喜若狂。再次感谢大家,祝其他遇到这个问题的人好运!在