@Maga : voici une proposition de complément de l'article. Si tu l'as l'occasion / la possibilité d'y jeter un coup d’œil... (peur d'une coquille supplémentaire)
Mea maxima culpa
Errare humanum est, perseverare diabolicum : j'ai fait une erreur. Pris dans mon élan je n'ai pas évoqué la question de la libération de la mémoire. En réalité, j'avais tort sur ma vision de la gestion de la mémoire par cTypes.
Mon idée était la suivante : cTypes n'évoque à aucun moment la question de la libération de la mémoire. J'ai supposé que c'est la stratégie de gestion de la mémoire de Python qui s'applique, et donc qu'il était en capacité de prendre en charge pour nous.
Las, le commentaire de GaMa, tape juste : quoiqu'il arrive ça pêche sur l'usage, car un pointeur renvoie vers une zone qui peut avoir elle-même d'autres pointeurs (voir ici et là). Il faut gérer côté Python cet aspect, en demandant côté Rust dans notre cas, la libération de la mémoire. Et il ne peut pas connaître du reste, toutes les tailles à désallouer (typiquement le tableau mais pour d'autres éléments, il connaît la taille).
Dans notre bibliothèque Rust, rajoutons donc le nécessaire :
Côté Python, c'est plus compliqué. On doit gérer désormais la durée de vie de la mémoire derrière le pointeur, mais l'appeler au bon moment pour ne pas désallouer côté Rust et utiliser côté Python.
Pour cela, le mieux que j'ai trouvé est d'avoir un objet intermédiaire dont on aura le signal de destruction. ("__del__"). C'est lui qui permettra de faire appel à la désallocation :
print("--- partie 3.2 - création d'un objet de retour de taille indéterminée à l'appel")classValeurRetour(Structure):# on n'utilise pas directement _fields_=[("a",c_int),("contenu",POINTER(c_int))]malib.test_array_retour_complexe.restype=POINTER(ValeurRetour)malib.liberer_valeurretour.argtypes=[POINTER(ValeurRetour),]# on généralise à toutes les valeurs retours classEnveloppeValeurRetourLibDistante():def__init__(self):self.obj_ptr=malib.test_array_retour_complexe(len(tableau))self.a=self.obj_ptr.contents.ac=self.obj_ptr.contents.contenuself.contenu=[c[i]foriinrange(0,self.a)]def__del__(self):# l'objet d'enveloppe de l'objet pointeur Python va être supprimé :# il déclenche donc la libération de la mémoire allouée par Rust malib.liberer_valeurretour(self.obj_ptr)obj_enveloppe=EnveloppeValeurRetourLibDistante()print("[PYTHON] ===>",obj_enveloppe.a,obj_enveloppe.contenu)
Cependant si vous vous ne souciez pas de ce point, pensez au décorateur "property" pour envelopper plus aisément l'appel à l'objet lié par le pointeur, dans l'objet d'enveloppe :
[^] # Re: memforget et fuite mémoire
Posté par JulienG . En réponse au journal cTypes + Rust = approfondir une relation d'amour et d'eau (fraîche). Évalué à 2.
@Maga : voici une proposition de complément de l'article. Si tu l'as l'occasion / la possibilité d'y jeter un coup d’œil... (peur d'une coquille supplémentaire)
Mea maxima culpa
Errare humanum est, perseverare diabolicum : j'ai fait une erreur. Pris dans mon élan je n'ai pas évoqué la question de la libération de la mémoire. En réalité, j'avais tort sur ma vision de la gestion de la mémoire par cTypes.
Mon idée était la suivante : cTypes n'évoque à aucun moment la question de la libération de la mémoire. J'ai supposé que c'est la stratégie de gestion de la mémoire de Python qui s'applique, et donc qu'il était en capacité de prendre en charge pour nous.
Las, le commentaire de GaMa, tape juste : quoiqu'il arrive ça pêche sur l'usage, car un pointeur renvoie vers une zone qui peut avoir elle-même d'autres pointeurs (voir ici et là). Il faut gérer côté Python cet aspect, en demandant côté Rust dans notre cas, la libération de la mémoire. Et il ne peut pas connaître du reste, toutes les tailles à désallouer (typiquement le tableau mais pour d'autres éléments, il connaît la taille).
Dans notre bibliothèque Rust, rajoutons donc le nécessaire :
Côté Python, c'est plus compliqué. On doit gérer désormais la durée de vie de la mémoire derrière le pointeur, mais l'appeler au bon moment pour ne pas désallouer côté Rust et utiliser côté Python.
Pour cela, le mieux que j'ai trouvé est d'avoir un objet intermédiaire dont on aura le signal de destruction. ("__del__"). C'est lui qui permettra de faire appel à la désallocation :
Si la comparaison d'attributs vous est importante, gardez à l'esprit que cTypes créée un nouvel objet interne à chaque appel à l'objet pointé, comme l'illustre l'exemple de la documentation :
Cependant si vous vous ne souciez pas de ce point, pensez au décorateur "property" pour envelopper plus aisément l'appel à l'objet lié par le pointeur, dans l'objet d'enveloppe :
Enfin vous pouvez retrouver très facilement le jeu de poupées imbriquées qui sert à la mécanique interne ("tout est objet en Python") :
Au premier niveau, on retrouve bien un objet "pointeur" à la sauce Python :
<__main__.LP_ValeurRetour object at 0x7fdd12f7f940>Qui permet d'accéder via ".contents" à l'objet "reconstitué" via la classe du même nom,
<__main__.ValeurRetour object at 0x7fdd12f7c6c0>Qui permet lui-même d'accéder à mon tableau au travers d'un autre pointeur :
<__main__.LP_c_int object at 0x7fdd12f7c3c0>Qui, devinez quoi ?, me permet d'accéder à ma valeur (la première case qui est à 42, puis de pouvoir d'itérer sur le tableau en mémoire).