Home > Enterprise >  Ctypes send an array of structures
Ctypes send an array of structures

Time:12-01

I have to realize a project between python and C. And one of the instructions is to use Ctypes. So I need to call my C function from python. And the latter needs me to send it two integer variables and a structure array. But I can't get the declaration to work. I don't know how to make the declaration.

Thank you very much for your answer

I have tried to search the internet for possibilities but I can't find anything conclusive.

C: `

int sac_dos_brute(int nb_produit, reference *tab, float masse) // Recupere le nombre de produits et un tableau de structures pour pouvoir gérrer l'expedition
{
    tri(tab, nb_produit);
    remplir_camion(tab,nb_produit,masse);

    return tab;

}

enter code here`

typedef struct    //Defini une structure qui a été envoyée par le code Python et qui contient uniquement les élements utiles
{
    int UID;
    float prix;
    float poids;
    int quantite;
    int nb_commande;
} reference ;

Python: `

from ctypes import *

dll = CDLL('C:/..../workplease.dll')



 class reference (Structure):
  _fields_ =[
      ('UID',c_int),
      ('prix',c_float),
      ('poids',c_float),
      ('quantite',c_int),
      ('nb_commande',c_int),
  ]








'''dll.sac_dos_brute.argtypes = [c_int, c_int, POINTER(reference)]

dll.sac_dos_brute(nb_produits, nb_camion, tab)'''


nb_produit=2

dll.sac_dos_brute.argtypes=[POINTER(reference)]

tab = [reference()]*2

tab[0].UID = 234
tab[0].prix= 12
tab[0].poids= 234
tab[0].quantite= 3
tab[0].nb_commande=1

tab[1].UID = 237
tab[1].prix= 15
tab[1].poids= 256
tab[1].quantite= 6
tab[1].nb_commande=2
    
dll.sac_dos_brute.argtype(c_int,c_int,c_, )
t = dll.sac_dos_brute(nb_produit,  byref(tab)) `

CodePudding user response:

I suspect the problem is that you were creating a list[reference] instead of Array[reference] which translates to tab = (reference * 2)(). Let's see if this works:

import ctypes as c

def sac_dos_brute(nb_produit: int, reference: c.Array[reference], masse: float) -> None:
    dll = c.cdll.LoadLibrary('C:/..../workplease.dll')
    dll.sac_dos_brute(c.c_int(nb_produit), c.byref(reference), c.c_float(masse))

nb_produit: int = 2
tab = (reference * 2)()
tab[0].UID = 234
tab[0].nom = b'Iphone'
tab[0].prix = 12.0
tab[0].poids = 234.0
tab[0].categorie = b'Telephone'
tab[0].marque = b'Apple'
tab[0].annee = 2020
tab[0].quantite = 3
tab[0].nb_commande = 1
tab[0].avis = 5
tab[1].UID = 237
tab[1].nom = b'Ipad'
tab[1].prix = 15.0
tab[1].poids = 256.0
tab[1].categorie = b'Tablette'
tab[1].marque = b'Apple'
tab[1].annee = 2022
tab[1].quantite = 6
tab[1].nb_commande = 2
tab[1].avis = 4
masse: float = 0.0

sac_dos_brute(nb_produit, tab, masse)

CodePudding user response:

Since C profile is

int sac_dos_brute(int nb_produit, reference *tab, float masse) ;

argtypes should be

dll.sac_dos_brute.argtypes = [c_int, POINTER(reference), c_float]

That one, I am sure you've already tried, and is false in your code just because you are in the process of trying many things. But just to be sure...

Then, the way you are creating the array is not correct.

[reference()]*2

is a call to reference(), which creates one structure, repeated in an array. You don't want a python list containing several structures (even less if it is several times the same one). You wan't an array to a reference, and to be more accurate, an array to 2 of them

So, it should be

tab = (reference * 2)()

(I see that you've already been told that while I was away)

But then, argument passing is way easier. You don't need byref here (byref is when you are passing a structure, for example, but want the C code to receive a pointer to it. You don't want that, since what you have, tab, is already an array. It will be passed as a pointer to function)

So, simply call

dll.sac_dos_brute(nb_produit, tab, masse)

So altogether (without any change to your C code, assuming that it reflects the change you've made since in your python code)

from ctypes import *

dll = CDLL('C:/..../workplease.dll')

class reference (Structure):
  _fields_ =[
      ('UID',c_int),
      ('prix',c_float),
      ('poids',c_float),
      ('quantite',c_int),
      ('nb_commande',c_int),
  ]

dll.sac_dos_brute.argtypes=[c_int, POINTER(reference), c_float]


nb_produit=2
tab = (reference*nb_produit)()

tab[0].UID = 234
tab[0].prix= 12
tab[0].poids= 234
tab[0].quantite= 3
tab[0].nb_commande=1

tab[1].UID = 237
tab[1].prix= 15
tab[1].poids= 256
tab[1].quantite= 6
tab[1].nb_commande=2

masse=3.14 # Just something I've chosen to fill the holes
    
t = dll.sac_dos_brute(nb_produit, tab, masse)

And of course, from there, you can't put back the fields you've removed, as long as you put them back in both (C and python) structure, and in the same order. For what I saw, you were using them correctly (using b-string, as you should).

  • Related