Question

[Mise à jour: Problème résolu! Voir bas du message]

J'ai besoin pour permettre aux développeurs de python de passer un tableau de données emballés (dans ce cas de sommets) dans mon API, qui est une série de C ++ interfaces exposées manuellement via l'API Python C. Ma première impression avec ceci est d'utiliser la ctypes classe Structure pour permettre une interface comme ceci:

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

Lorsque la fonction que je suis en train de passer à a la signature suivante:

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

Et l'emballage Python ressemble à ceci:

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

Bien sûr, le plus gros problème que je pose est la suivante: je peux récupérer le PyObject pour le struct, mais je ne sais pas comment je jetais au type correct. Le code ci-dessus échoue lamentablement. Alors, comment exactement ce que j'aborderais permettant à l'utilisateur de me transmettre ce type de données à partir de Python?

Maintenant, deux choses à considérer: La première est que je l'ai déjà fait un peu de mon Python / C ++ couche écrite, et je suis parfaitement heureux avec elle (j'éloigné de SWIG pour que je puisse avoir plus de flexibilité). Je ne veux pas recoder, donc je préférerais une solution qui fonctionne avec l'API C en mode natif. Deuxièmement, je ne l'intention d'avoir la structure Vertex est pré-définie dans mon code C ++, donc je préférerais ne pas avoir l'utilisateur besoin de re-définir dans le Python (réduit sur les erreurs de cette façon), mais je suis pas sûr de savoir comment exposer une structure contiguë comme ça. Troisièmement, je n'ai aucune raison d'essayer la structure de ctypes côté de ne pas connaître une autre façon de le faire. Toutes les suggestions sont les bienvenus. Enfin, puisque c'est (comme vous l'aurez deviné) pour une application graphique, je préfère une méthode plus rapide sur une pratique, même si la méthode plus rapide prend un peu plus de travail.

Merci pour toute aide! Je me sens toujours mon chemin extensions python, il est donc d'une grande aide pour obtenir la participation communautaire sur certaines des parties collantes.

[SOLUTION]

Alors d'abord, merci à tous ceux qui campèrent dans leurs idées. Il y avait beaucoup de petites bouchées qui a ajouté à la réponse éventuelle. En fin de compte est ici ce que je trouve: la suggestion de Sam d'utiliser struct.pack a fini par avoir raison sur l'argent. Voyant que j'utilise Python 3, je devais ruser tant soit peu, mais quand tout a été dit et fait cela en fait eu un triangle montrant sur mon écran:

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)

Avec mon analyse tuple cherche maintenant comme ceci:

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

Notez que même si je ne pas utiliser la variable len dans cet exemple (si je vais dans le produit final) je dois analyser le tuple en utilisant « y # » au lieu de simplement « y » ou bien elle s'arrêtera à la première valeur NULL (selon la documentation). Il faut également considérer: void * moulages de ce genre sont très dangereux, donc s'il vous plaît ne charge plus de contrôle d'erreurs que je montre ici

Alors, le travail bien fait, jour heureux, plier bagage et rentrer à la maison, oui?

Attendez! Pas si vite! Il y a plus!

Se sentir bien sur la façon que tout a parfaitement fonctionné, j'ai décidé, sur un coup de tête, pour voir si ma précédente tentative encore fait exploser sur moi et revenue au premier extrait de python dans ce post. (En utilisant le nouveau code C, bien sûr) et ... ça a marché! Les résultats sont identiques à la version struct.pack! Hou la la!

Cela signifie que vos utilisateurs ont le choix dans la façon dont ils vont fournir ce type de données, et votre code peut gérer soit sans changement. Personnellement, je vais encourager la méthode ctype.Structure, car je pense qu'il est plus facile pour une meilleure lisibilité, mais vraiment il est tout ce que l'utilisateur est à l'aise. (Heck, ils pourraient taper manuellement une chaîne d'octets en hexadécimal s'ils voulaient. Il fonctionne. J'ai essayé.)

Honnêtement, je pense que c'est le meilleur résultat possible, donc je suis extatique. Merci encore à tous, et bonne chance à quelqu'un d'autre qui se jette dans ce problème!

Était-ce utile?

La solution

Non testé mais vous devriez essayer ce et laissez-nous savoir si son assez rapide pour vos besoins.

Du côté python, emballez les sommets dans une chaîne au lieu d'un objet.

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

Sur l'emballage bibliothèque / python C, modifier votre PyArgs_ParseTuple utiliser la chaîne de format "si". Cela vous permet de convertir votre chaîne de python dans une chaîne C (char *) que vous pouvez ensuite catalogué comme un pointeur vers votre struct vecteur. A ce stade, la chaîne C est un flux d'octets / mots / flotteurs et devrait être ce que vous cherchez.

Bonne chance!

Autres conseils

La chose la plus facile que je peux voir à faire serait tout simplement éviter la question tout à fait et exposer une Device_ReadVertex qui prend en x, y, z, u, v et la couleur comme arguments. Cela présente des inconvénients évidents, comme faire les programmeurs Python alimentent vertices un par un.

Si ce n'est pas assez bon (semble probable qu'il n'est pas), alors vous pouvez essayer de définir un nouveau type Python comme décrit ici . Il est un peu plus de code, mais je pense que c'est la méthode « son plus architecturalement », parce que vous assurer que vos développeurs de Python utilisent la même définition de type que vous êtes dans le code C. Il permet également un peu plus de flexibilité qu'un struct simple (il est vraiment une classe, avec la possibilité d'ajouter des méthodes, etc.), que je ne suis pas sûr que vous avez réellement besoin, mais il pourrait être utile plus tard.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top