5 votes

Allocation de tableau de type SWIG C++ à partir de Python

Je suis en train d'écrire un script python pour un programme qui a exposé son API C++ en utilisant SWIG. Une fonction exposée par SWIG a une interface comme ceci :

void writePixelsRect(JoxColor* colors, int left, int top, int width, int height);

JoxColor est une structure POD ressemblant à ceci :

struct JoxColor {
    float r, g, b, a;
};

Je peux facilement créer un seul JoxColor en Python et invoquer un appel à writePixelsRect de cette manière :

c = JoxApi.JoxColor()
c.r = r
c.g = g
c.b = b
c.a = a
JoxApi.writePixelsRect(c, x, y, 1, 1)

Appeler de manière répétée writePixelsRect avec un rectangle de 1x1 pixel est très lent, donc je veux créer un tableau de JoxColor en python pour pouvoir écrire de plus grands rectangles en une seule fois. Est-ce possible avec les types SWIG ?

Notez que je n'ai pas accès au code source de la bibliothèque C++ exposant JoxColor et writePixelsRect donc je ne peux pas ajouter de fonction d'aide pour cela. Je ne veux pas non plus introduire de nouveau code C++ dans le système car cela obligerait les utilisateurs de mon script python à compiler le code C++ sur la plateforme sur laquelle ils s'exécutent. J'ai cependant accès à ctypes dans l'environnement python donc si je pouvais d'une manière ou d'une autre caster un tableau de flottants créé dans ctypes au type de JoxColor* pour SWIG, cela fonctionnerait pour moi.

2voto

gsteff Points 1674

C'est un peu délicat, mais pour cette partie du code, pourriez-vous au moins utiliser une solution pure-ctypes? En gros, examinez manuellement les symboles exportés par le fichier de bibliothèque partagée pour trouver le nom sous lequel la fonction writePixelsRect a été exportée. En C++, il y a de la surcharge de nom, donc même si cela pourrait être simplement writePixelsRect si les auteurs de la bibliothèque ont choisi de le faire avec extern "C", il pourrait être quelque chose de beaucoup plus compliqué, comme _Z15writePixelsRectP8JoxColoriiii (c'est ainsi qu'il a été exporté dans une fausse bibliothèque C++ que je viens de créer sur mon système).

Sur Linux, cette commande devrait vous donner le nom du symbole:

nm libjox.so | grep writePixel | cut -d " " -f 3

Ensuite, sauvegardez cette chaîne et insérez-la dans du code Python un peu comme ceci:

from ctypes import *

LIBRARY_NAME = 'libjox.so'
c = cdll.LoadLibrary(LIBRARY_NAME)

WIDTH = 20
HEIGHT = 20

class JoxColor(Structure):
    _fields_ = [("r", c_float), ("g", c_float), ("b", c_float), ("a", c_float)]

ColorBlock = JoxColor * (WIDTH * HEIGHT)

data_array = ColorBlock()

color = JoxColor(0, 0, 1, 0)
for i in range(WIDTH * HEIGHT):
    data_array[i] = color

c._Z15writePixelsRectP8JoxColoriiii(data_array, 0, 0, WIDTH, HEIGHT)

En supposant que _Z15writePixelsRectP8JoxColoriiii est le symbole sous lequel la fonction est accessible dans la bibliothèque partagée. Ce code a fonctionné sur mon système avec une fausse bibliothèque comme celle-ci:

#include 

struct JoxColor {
    float r, g, b, a;
};

void writePixelsRect(JoxColor *colors, int left, int top, int width, int height) {
    int p = 0;
    printf("size: %i, %i\n", width, height);
    for (int i = 0; i < width; i++) {
        for (int j = 0; j < height; j++) {
            JoxColor color = colors[p];
            printf("pixel: %f, %f, %f, %f\n", color.r, color.g, color.b, color.a);
        }
    }
}

Je suis donc optimiste que ce n'est pas trop éloigné du code fonctionnel dans votre environnement.

0voto

Adam Points 7110

À l'exception des typemaps spéciaux, ce prototype SWIG

void writePixelsRect(JoxColor* colors, int left, int top, int width, int height);

signifie que colors est un seul objet de type JoxColor, pas un tableau. Le fait que votre appel avec un seul objet fonctionne (bien que lentement) suggère que c'est vrai. Donc, passer un tableau va probablement simplement vous donner une erreur de type de correspondance à partir du code de wrapper SWIG.

Mais honnêtement, cela ressemble à une fonction qui écrit un rectangle de taille arbitraire. Donc, si vous voulez dessiner un plus grand rectangle (d'une seule couleur), passez simplement une largeur et/ou une hauteur plus grande :

JoxApi.writePixelsRect(c, x, y, 10, 20)

Éditer: Je n'avais pas réalisé que vous écriviez le wrapper SWIG, je pensais que cela vous avait été fourni. Dans ce cas, vous pouvez écrire un typemap qui convertira une liste Python (ou un tuple, ou ce que vous voulez) en JoxColor*. La documentation de SWIG montre un exemple pour transformer une liste de chaînes Python en char** : http://www.swig.org/Doc1.3/Python.html#Python_nn59 Le typemap utilise les API C Python pour effectuer la conversion, vous pouvez utiliser tout ce que disent les documents Python. Essentiellement, vous allouez un tableau JoxColor puis vous itérez sur l'objet liste Python et utilisez PyList_GetItem pour obtenir chaque objet individuel. Cela renverra un PyObject enveloppé par SWIG, vous pouvez utiliser SWIG_ConvertPtr(list_item_py_object, (void**)&joxcolor_ptr, $descriptor(JoxColor *), 0) pour convertir cela en un pointeur vers votre élément JoxColor réel. Ensuite, vous pouvez le copier dans votre tableau.

N'oubliez pas qu'un typemap pour JoxColor* s'appliquera partout où JoxColor* apparaît, vous pouvez dire JoxColor* colors pour le spécialiser à ce cas particulier.

FYI, par défaut, SWIG enveloppe JoxColor*, JoxColor&, JoxColor et JoxColor[] de la même manière, en tant qu'objet unique. Python ne dispose que d'objets, il ne connaît pas les pointeurs/références/tableaux (les listes Python sont également des objets). http://www.swig.org/Doc1.3/Python.html#Python_nn22

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X