Вопрос

[Обновлять:Задача решена!Смотрите нижнюю часть поста]

Мне нужно разрешить разработчикам Python передавать массив упакованных данных (в данном случае вершин) в мой API, который представляет собой серию интерфейсов C++, предоставляемых вручную через API Python C.Мое первое впечатление заключается в использовании класса структуры ctypes, позволяющего создать такой интерфейс:

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, так как считаю, что он облегчает читаемость, но на самом деле это то, что удобно пользователю.(Черт возьми, они могли бы вручную набрать строку байтов в шестнадцатеричном формате, если бы захотели.Оно работает.Я пытался.)

Честно говоря, я думаю, что это лучший возможный результат, поэтому я в восторге.Еще раз спасибо всем и удачи всем, кто столкнется с этой проблемой!

Это было полезно?

Решение

Не тестировалось, но вам следует попробовать и сообщить нам, достаточно ли это быстро для ваших нужд.

На стороне Python упакуйте вершины в строку, а не в объект.

str = "" # byte stream for encoding data
str += struct.pack("5f i", vert1.x, vert1.y, vert1.z, vert1.u, vert1.v, vert1.color) # 5 floats and an int
# same for other vertices

device. ReadVertices( verts, 3) # send vertices to C library

В библиотеке C/оболочке Python измените PyArgs_ParseTuple, чтобы использовать строку формата. "si".Это преобразует вашу строку Python в строку C (char*), которую вы затем сможете привести к типу в качестве указателя на вашу векторную структуру.На этом этапе строка C представляет собой поток байтов/слов/числов с плавающей запятой и должна быть тем, что вы ищете.

Удачи!

Другие советы

Самое простое, что я могу сделать, — это вообще избежать этой проблемы и предоставить Device_ReadVertex, который принимает x, y, z, u, v и цвет в качестве аргументов.У этого решения есть очевидные недостатки: например, программистам Python приходится передавать ему вершины одну за другой.

Если этого недостаточно (вероятно, это не так), вы можете попробовать определить новый тип Python, как описано. здесь.Это немного больше кода, но я думаю, что это «более архитектурно обоснованный» метод, поскольку вы гарантируете, что ваши разработчики Python используют то же определение типа, что и вы в коде C.Это также обеспечивает немного большую гибкость, чем простая структура (на самом деле это класс с возможностью добавления методов и т. д.), что, я не уверен, вам действительно нужно, но может пригодиться позже.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top