2017-08-24 3 views
0

Je travaille avec une structure ctypes fournie en externe qui a des champs définis comme des champs fourre-tout à usage général. Le type du champ data est basé sur la valeur du champ mode, comme on le voit dans cet exemple:Propre moyen d'insérer la représentation en octets d'un flottant IEEE dans un champ Python ctypes uint32?

CONTAINS_ONE_FLOAT = 1 

class TestStruct(ctypes.Structure): 
    pass 

TestStruct._fields_ = [ 
    ('mode', ctypes.c_uint32), 
    ('data', ctypes.c_uint32 * 2) 
] 

je dois stocker une simple précision (32 bits) Valeur de virgule flottante data chaque fois que mode est défini sur CONTAINS_ONE_FLOAT. Le deuxième mot de data n'est pas utilisé dans ce mode.

Je n'ai jusqu'ici été capable de comprendre cette approche en utilisant des ctypes de coulée. Il semble WAY trop verbeux, mais cela fonctionne.

float_value = 42.42 
float_value_c = ctypes.c_float(float_value) 
float_value_cp = ctypes.POINTER(ctypes.c_float)(float_value_c) 
int_val_p = ctypes.cast(float_value_cp, ctypes.POINTER(ctypes.c_uint32)) 
int_val = int_val_p.contents 

x = TestStruct() 
x.mode = CONTAINS_ONE_FLOAT 
x.data[0] = int_val 

print("Should be 1110027796: " + str(x.data[0])) 

Existe-t-il une meilleure façon de faire cela qui ne nécessite pas 5 étapes?

+1

Sur une ligne, vous pouvez utiliser 'ctypes.c_float.from_buffer (x.data) .value = 42.42'. – eryksun

Répondre

1

Vous pouvez utiliser struct:

float_bytes = struct.pack('=f', float_value) 
x.data[0] = struct.unpack('=I', float_bytes)[0] 
+0

J'ai dû ajouter '[0]' à la fin de la deuxième instruction puisque 'unpack()' retourne un tuple. Sinon, cela fonctionne et ne nécessite pas de modifier la structure sous-jacente, qui me est fournie et qui n'est pas facilement éditable. –

1

Le modèle que vous décrivez est normalement mis en œuvre en C avec un union; vous pouvez faire la même chose avec ctypes:

CONTAINS_ONE_FLOAT = 1 

class TestStructData(ctypes.Union): 
    _fields_ = [ 
     ('one_float', ctypes.c_float), 
     ('two_dword', ctypes.c_uint32 * 2) 
    ] 

class TestStruct(ctypes.Structure): 
    _fields_ = [ 
     ('mode', ctypes.c_uint32), 
     ('data', TestStructData) 
    ] 

Pour lire/écrire votre flotteur (ou tout autre type il y a), lecture/écriture dans le champ approprié de TestStructData.

+0

C'est la meilleure réponse w.r.t. la conception originale des structures, mais malheureusement les miennes sont fournies à l'extérieur et je ne peux pas les changer pour inclure l'union. J'ai fait une expérience de chronométrage et c'était environ deux fois plus rapide que la solution struct pack/unpack. J'ai également essayé d'utiliser une union séparée juste pour la conversion, mais c'était à peu près aussi rapide que struct pack/unpack. –