[Обновление: проблема решена! Смотрите нижнюю часть поста]
Мне нужно разрешить разработчикам python передавать массив упакованных данных (в данном случае вершин) в мой API, который представляет собой серию интерфейсов C ++, предоставляемых вручную через Python C API. Первоначально у меня сложилось впечатление, что я использую класс 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
Где функция, которую я пытаюсь передать, имеет следующую подпись:
void Device::ReadVertices(Vertex* verts, int count);
А оболочка 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<Vertex*>(PyCObject_AsVoidPtr(py_verts));
self->device->ReadVertices(verts, count);
Py_RETURN_NONE;
}
Конечно, самая большая проблема, с которой я столкнулся, заключается в следующем: я могу получить PyObject для структуры, но я понятия не имею, как я могу привести его к правильному типу. Приведенный выше код с треском проваливается. Так как же мне разрешить пользователю передавать мне такие данные из Python?
Теперь несколько вещей, которые следует учесть: во-первых, у меня уже есть довольно много написанного слоя Python / C ++, и я совершенно доволен им (я отошел от SWIG, чтобы иметь больше гибкости). Я не хочу перекодировать его, поэтому я бы предпочел решение, которое изначально работает с C API. Во-вторых, я намереваюсь, чтобы структура Vertex была предопределена в моем коде C ++, поэтому я бы предпочел, чтобы у пользователя не было необходимости переопределять его в Python (таким образом сокращается количество ошибок), но я не уверен, как выставить непрерывную структуру, как это. В-третьих, у меня нет причин пытаться структурировать ctypes, не зная другого способа сделать это. Любые предложения приветствуются. Наконец, поскольку это (как вы уже догадались) для графического приложения, я бы предпочел более быстрый метод, чем удобный, даже если более быстрый метод требует немного больше работы.
Спасибо за любую помощь! Я до сих пор разбираюсь в расширениях Python, так что это большая помощь, чтобы получить мнение сообщества по некоторым из самых липких частей.
[РЕШЕНИЕ]
Итак, во-первых, спасибо всем, кто высказал свои идеи. Было много маленьких лакомых кусочков, которые суммировали с возможным ответом. В конце концов, вот что я нашел: предложение Сэма использовать struct.pack в итоге оказалось правильным на деньги. Видя, что я использую 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<Vertex*>(py_verts);
self->device->ReadVertices(verts, count);
Py_RETURN_NONE;
}
Обратите внимание, что хотя я не использую переменную len
в этом примере (хотя я буду использовать в конечном продукте), мне нужно проанализировать кортеж, используя 'y #' вместо просто 'y', иначе остановка на первом NULL (согласно документации). Также следует учитывать: void * приведение таких типов довольно опасно, поэтому, пожалуйста, загрузите больше ошибок, чем я показываю здесь!
Итак, работа хорошо сделана, счастливого дня, соберись и иди домой, да?
Подождите! Не так быстро! Там БОЛЬШЕ!
Чувствуя себя хорошо, как все это сработало, я решил по прихоти посмотреть, все ли моя предыдущая попытка все-таки обернулась и вернулась к первому фрагменту python в этом посте. (Используя новый код на C, конечно) и ... это сработало! Результаты были идентичны версии struct.pack! Вот Это Да!
Таким образом, это означает, что у ваших пользователей есть выбор, как они будут предоставлять данные такого типа, и ваш код может обрабатывать их без изменений. Лично я собираюсь поощрять метод ctype.Structure, так как я думаю, что он облегчает читабельность, но на самом деле это то, с чем пользователю удобно. (Черт, они могли бы вручную напечатать строку байтов в шестнадцатеричном виде, если бы захотели. Это работает. Я пытался.)
Честно говоря, я думаю, что это наилучший из возможных результатов, поэтому я в восторге. Еще раз спасибо всем и удачи всем, кто сталкивается с этой проблемой!