Frage

[Aktualisieren:Problem gelöst!Siehe Ende des Beitrags]

Ich muss Python-Entwicklern erlauben, ein Array gepackter Daten (in diesem Fall Vertices) an meine API zu übergeben, bei der es sich um eine Reihe von C++-Schnittstellen handelt, die manuell über die Python-C-API bereitgestellt werden.Mein erster Eindruck dabei ist, die ctypes-Strukturklasse zu verwenden, um eine Schnittstelle wie diese zu ermöglichen:

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

Wobei die Funktion, an die ich übergeben möchte, die folgende Signatur hat:

void Device::ReadVertices(Vertex* verts, int count);

Und der Python-Wrapper sieht in etwa so aus:

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;
}

Das größte Problem, das ich habe, ist natürlich Folgendes:Ich kann das PyObject für die Struktur abrufen, habe aber keine Ahnung, wie ich es in den richtigen Typ umwandeln würde.Der obige Code schlägt kläglich fehl.Wie genau würde ich also vorgehen, damit der Benutzer mir diese Art von Daten aus Python weitergeben kann?

Nun ein paar Dinge, die Sie beachten sollten:Erstens habe ich bereits einen Großteil meiner Python/C++-Ebene geschrieben und bin vollkommen zufrieden damit (ich bin von SWIG weggegangen, um mehr Flexibilität zu haben).Ich möchte es nicht neu codieren, daher würde ich eine Lösung bevorzugen, die nativ mit der C-API funktioniert.Zweitens beabsichtige ich, die Vertex-Struktur in meinem C++-Code vordefiniert zu haben, sodass ich es vorziehen würde, wenn der Benutzer sie nicht in Python neu definieren muss (auf diese Weise werden Fehler vermieden), aber ich bin es Ich bin mir nicht sicher, wie man eine zusammenhängende Struktur wie diese freilegt.Drittens habe ich keinen Grund, die ctypes-Struktur auszuprobieren, abgesehen davon, dass ich keinen anderen Weg kenne, dies zu tun.Alle Vorschläge sind willkommen.Da es sich hierbei (wie Sie vielleicht vermutet haben) um eine Grafik-App handelt, würde ich schließlich eine schnellere Methode einer bequemen vorziehen, auch wenn die schnellere Methode etwas mehr Arbeit erfordert.

Vielen Dank für jede Hilfe!Ich bin immer noch dabei, mich mit Python-Erweiterungen vertraut zu machen, daher ist es eine große Hilfe, Community-Beiträge zu einigen der schwierigeren Teile zu erhalten.

[LÖSUNG]

Zunächst einmal vielen Dank an alle, die ihre Ideen eingebracht haben.Es waren viele kleine Details, die schließlich zur Antwort führten.Am Ende habe ich Folgendes gefunden:Sams Vorschlag, struct.pack zu verwenden, war am Ende genau das Richtige.Da ich Python 3 verwende, musste ich es ein wenig anpassen, aber am Ende erschien tatsächlich ein Dreieck auf meinem Bildschirm:

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)

Mein Tupel-Parsing sieht jetzt so aus:

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;
}

Beachten Sie, dass, auch wenn ich das nicht verwende len Variable in diesem Beispiel (obwohl ich das im Endprodukt tun werde) muss ich das Tupel mit „y#“ statt nur mit „y“ analysieren, sonst stoppt es beim ersten NULL (laut Dokumentation).Zu berücksichtigen sind außerdem:void*-Umwandlungen wie diese sind ziemlich gefährlich, also führen Sie bitte viel mehr Fehlerprüfungen durch, als ich hier zeige!

Also, gute Arbeit geleistet, schönen Tag, einpacken und nach Hause gehen, ja?

Warten!Nicht so schnell!Es gibt mehr!

Mit gutem Gefühl, wie das alles geklappt hat, beschloss ich aus einer Laune heraus zu sehen, ob mein vorheriger Versuch immer noch scheiterte, und kehrte zum ersten Python-Ausschnitt in diesem Beitrag zurück.(Natürlich mit dem neuen C-Code) und...es funktionierte!Die Ergebnisse waren identisch mit der struct.pack-Version!Wow!

Das bedeutet also, dass Ihre Benutzer die Wahl haben, wie sie diese Art von Daten bereitstellen möchten, und Ihr Code kann beides ohne Änderungen verarbeiten.Persönlich werde ich die ctype.Structure-Methode empfehlen, da ich denke, dass sie die Lesbarkeit erleichtert, aber eigentlich ist es alles, womit der Benutzer vertraut ist.(Verdammt, sie könnten manuell eine Folge von Bytes im Hexadezimalformat eingeben, wenn sie wollten.Es klappt.Ich habe es versucht.)

Ehrlich gesagt denke ich, dass dies das bestmögliche Ergebnis ist, also bin ich begeistert.Nochmals vielen Dank an alle und viel Glück für alle anderen, die auf dieses Problem stoßen!

War es hilfreich?

Lösung

Nicht getestet, aber Sie sollten es ausprobieren und uns mitteilen, ob es schnell genug für Ihre Anforderungen ist.

Packen Sie auf der Python-Seite die Scheitelpunkte in einen String statt in ein Objekt.

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

Ändern Sie im C-Bibliotheks-/Python-Wrapper Ihr PyArgs_ParseTuple, um die Formatzeichenfolge zu verwenden "si".Dadurch wird Ihr Python-String in einen C-String (char*) umgewandelt, den Sie dann als Zeiger auf Ihre Vektorstruktur umwandeln können.An diesem Punkt ist der C-String ein Strom von Bytes/Worten/Floats und sollte das sein, wonach Sie suchen.

Viel Glück!

Andere Tipps

Das Einfachste, was ich mir vorstellen kann, wäre, das Problem ganz zu vermeiden und einen Device_ReadVertex bereitzustellen, der x, y, z, u, v und color als Argumente akzeptiert.Dies hat offensichtliche Nachteile, wie zum Beispiel, dass die Python-Programmierer die Scheitelpunkte einzeln füttern müssen.

Wenn das nicht gut genug ist (was wahrscheinlich nicht der Fall ist), können Sie versuchen, wie beschrieben einen neuen Python-Typ zu definieren Hier.Es ist etwas mehr Code, aber ich denke, dass dies die „architektonischere“ Methode ist, da Sie sicherstellen, dass Ihre Python-Entwickler dieselbe Typdefinition verwenden wie Sie im C-Code.Es ermöglicht auch etwas mehr Flexibilität als eine einfache Struktur (es ist eigentlich eine Klasse mit der Möglichkeit, Methoden usw. hinzuzufügen), die Sie meiner Meinung nach nicht wirklich benötigen, aber später vielleicht nützlich sein könnte.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top