Seite 1 von 1

ctypes union als rückgabewert

Verfasst: Donnerstag 12. Januar 2006, 09:10
von Thomas_S
Hallo zusammen,

Ich greife mit Python auf eine Dll (testapi.dll) zu und nutze dabei ctypes, das klappt auch alles soweit ganz gut, nur möchte ich jetzt eine Funktion in der Dll nutzen, die eine Union als Übergabewert verlangt nutzen und genau daran scheitere ich. Mache ich da irgendeinen (oder mehrere) Fenkfehler? Anbei folgt die Funktion auf die ich zugreifen möchte, die Union-Beschreibung, und ein Beispiel eines fehlgeschlagenen Versuchs das Problem zu lösen. (Weiß nicht ob das relevant ist, aber ich nutze ctypes-0.6.3 und Python 2.2.1 das muss auch leider so bleiben) Wäre toll wenn ihr mir dabei weiterhelfen könntet.

Die Funktion sieht so aus:

Code: Alles auswählen

bool ReadObject(TAsap3Hdl hdl, TModulHdl module,
                   const char *ObjectName, TFormat format,
                   TObjectValue * value);

Die Union beschreibt sich laut api wie folgt:				   
				   
typedef union {

  TValueType type;

  struct {
    TValueType type;

    double value; 
  } value;       

  struct {        
    TValueType type;

    short dimension;     
    double*       axis;   
  } axis;     

  struct {       
    TValueType type;      

    short len;             
    char*          ascii;   
  } ascii;      

  struct {
    TValueType type;

    short dimension;       
    double*       axis;   
    double*       values;  
  } curve;     
               
  struct {
    TValueType type;

    short xDimension;      
    short yDimension;     
    double*       xAxis;  
    double*       yAxis; 
    double*       values; 
  } map;       
} TObjectValue;

typedef enum {
  VALUE = 0, 
  CURVE = 1, 
  MAP   = 2, 
  AXIS  = 3,
  ASCII = 4
} TValueType;
Einer meiner Versuche das ganze zu lösen sieht so aus:

Code: Alles auswählen

>>> class value(Structure):
...     _fields_=[
...     ("ValueType", c_int),("value", c_double)]
...     
>>> class tcov(Union):
...     _fields_=[("ValueType", c_int),("value", value)]
...     
>>> co_val=tcov()
>>> windll.testapi.ReadObject(b, a, "channel1", 1, byref(co_val))
1								    <- die Funktion antwortet brav mit einem True
>>> print co_val.value.value
8.74235474698e-317 <- das ist jedoch nicht der erwartete Wert, es müste was mit 11.95 ungefähr rausspringen

Verfasst: Donnerstag 12. Januar 2006, 23:17
von BlackJack
Ich bezweifele das die Union so aussieht wie Du sie dargestellt hast. Die einzelnen Komponenten einer Union sind die einzelnen Auswahlmöglichkeiten und die erste Möglichkeit ist ``TValueType type;`` was als Information alleinstehend total unbrauchbar ist.

Normalerweise würde man so etwas erwarten:

Code: Alles auswählen

typedef struct {
    int type;
    union {
       struct {
          /* ... */
       } bar;
       struct {
          /* ... */
       } baz;
       struct {
          /* ... */
       } frobz;
       /* ... */
    } u;
} foo;
Je nach `type` kann man dann entscheiden ob man auf `bar`, `baz` etc. zugreifen muss.

Verfasst: Freitag 13. Januar 2006, 08:07
von Gast
Der erste Eintrag "TValueType type" ist nicht unbrauchbar, dieser erscheint ja auch wieder in den nachfolgenden Strukturen. Die Union ist ja nur ein vorbereiteter Speicherbereich, der die Größe der größten Struktur (speichermäßig) abbildet. Wird diese Union nun mit einem Eintrag belegt, so weiß man über die Abfrage von TValueType, welche Struktur zu verwenden ist. Das dann TValueType auch in den nachfolgenden Strukturen auftauchen muss ist auch klar, denn der Speicherbereich am Anfang der Union ist ja schon durch diesen Eintrag belegt.

kurz gesagt: die Union sieht so aus, hab ich der API so entnommen

Verfasst: Freitag 13. Januar 2006, 08:09
von Thomas_S
Hatte vergessen mich einzuloggen, der vorherige Eintrag war von mir.

Verfasst: Freitag 13. Januar 2006, 22:40
von BlackJack
Anonymous hat geschrieben:Der erste Eintrag "TValueType type" ist nicht unbrauchbar, dieser erscheint ja auch wieder in den nachfolgenden Strukturen.
Okay, dann mach aus "unbrauchbar" ein "überflüssig".

Ich habe so eine Union in der Form das erste mal gesehen. Die Form die ich beschrieben habe dagegen schon öfter. Es kommt vom Speicherlayout auf's gleiche hinaus, man spart sich aber das Wiederholen der Typinformation in jeder alternativen Struktur.

Verfasst: Samstag 14. Januar 2006, 03:18
von Joghurt
BlackJack hat geschrieben:Okay, dann mach aus "unbrauchbar" ein "überflüssig".
Naja, so kannst du

Code: Alles auswählen

switch (foobar.type)
anstatt

Code: Alles auswählen

switch (foobar.value.type)
schreiben

Verfasst: Samstag 14. Januar 2006, 11:20
von Thomas_S
Okay, dann mach aus "unbrauchbar" ein "überflüssig".
Der Meinung bin ich ja auch, doch an der dll bzw der api kann ich nichts ändern.

So, mein Problem ist mehr oder weniger gelöst. Nochmal zur Info, mein Wunsch war es eine Funktion einer DLL aufzurufen, die als Übergabewert einen Zeiger auf eine Union verlangt. Die möglichen Elemente der Union sind Strukturen, die jeweils aus einem Integer und dem ein oder anderen weiteren Datentyp (Double short etc.) bestehen und genau dort liegt jetzt der Knackpunkt. Ich habe mal eine Struktur aus einem c_int und einem c_double erzeugt. Die größe dieser Struktur müsste ja eigentlich 12 Bytes betragen (4 für c_int und 8 für c_double), doch wenn ich mir über sizeof(name der Struktur) die Größe anzeigen lasse, besteht die Struktur aus 16 Bytes.
Wenn ich nun diese Struktur verwende, um die vorher angesprochene Funktion aufzurufen, wir der int-Wert in den ersten 4 Bytes abgelegt und der double-Wert in den folgenden 8-Bytes d.h., der double-Wert befindet sich an der falschen Position. Lasse ich mir anschließend den double-Wert anzeigen, so ist klar, das dabei nur Müll herauskommen kann.

Meine nächste Frage wäre jetzt: Darf das denn überhaupt so sein? Und ist das so gewollt, dass die Struktur so erzeugt wird?

Sagt bescheid, falls ich mich unklar ausdrücke, bzw. falls ich notwendige Infos vergessen haben sollt.

PS: danke für die schnellen Kommentare

Verfasst: Samstag 14. Januar 2006, 23:17
von BlackJack
Kann es sein das Du einen 64 bittigen Prozessor in Deinem Rechner hast? Dann könnte es sein das ein C Compiler, und damit auch `ctypes`, auf die Idee kommen kann den `double` Wert so auszurichten. Bei mir ist so eine Struktur jedenfalls nur 12 Bytes gross.

Verfasst: Sonntag 15. Januar 2006, 11:00
von Thomas_S
Ich habe keinen 64-bit Rechner. Als Beispiel mal ein Auszug aus dem was bei mir passiert.

Code: Alles auswählen

>>> from ctypes import *
>>> class a(Structure):
...     _fields_=[
...     ("b", c_int),
...     ("c", c_double)
...     ]
...     
>>> sizeof(a)
16
>>>
Da ist doch der Wurm drin, oder mache ich irgendetwas falsch?

Verfasst: Sonntag 15. Januar 2006, 23:40
von BlackJack
Wo hast Du denn das `ctypes` Modul her? Ist das vielleicht für eine 64-Bit Architektur übersetzt worden?

Verfasst: Montag 16. Januar 2006, 08:21
von Thomas_S
Mein ctypes habe ich unter:
http://prdownloads.sourceforge.net/ctyp ... e?download bezogen.

Übrigens liefert folgendes das gleiche Ergebnis:

Code: Alles auswählen

>>> import struct
>>> a=struct.pack("id",1,2)
>>> len(a)
16
>>>
Kann mir vielleicht jemand sagen, ob das nun so sein soll?

Verfasst: Montag 16. Januar 2006, 21:49
von BlackJack
`struct` richtet die Daten so aus wie der C Compiler mit dem der Python-Interpreter übersetzt wurde. Wäre jetzt also die nächste Frage wo Du Deinen Python-Interpreter her hast und mit welchem Compiler auf welcher Architektur der übersetzt wurde.

Verfasst: Dienstag 17. Januar 2006, 09:00
von Thomas_S
Ich habe mal folgendes ausprobiert:

>>> a=struct.pack("id",1,2)
>>> len(a)
16
>>> a=struct.pack("<id",1,2)
>>> len(a)
12

Der untere Fall ist ja genau der, den ich über ctypes realisieren möchte. Gibt es bei ctypes irgendeine Möglichkeit auf byteorder, size und alignment Einfluß zu nehmen?? In der Dokumentation von ctypes habe ich leider nichts finden können. Aber vielleicht gibt es ja noch eine andere Möglichkeit, die ich noch nicht berücksichtigt habe.

Re: ctypes union als rückgabewert

Verfasst: Mittwoch 26. Juni 2013, 07:43
von wyattroerb
Hallo Thomas,
hab das gleiche Problem mit der CANapAPI.dll. Hast du damals eine Lösung gefunden?
Ich wäre dir für eine Antwort sehr dankbar!

Robert

Re: ctypes union als rückgabewert

Verfasst: Freitag 28. Juni 2013, 10:59
von jerch
@wyattroerb:
Als Quickfix fallen mir 2 Möglichkeiten ein:
- ein dünner C-Wrapper, der das "falsche" Alignment korrigiert und Deine Typen für ctypes benutzbar macht
- die Typengrößen und Offsets bestimmen und händisch mit struct auslesen

Für letzteres verlierst Du zwar den ctypes-Komfort, allerdings kannst Du Dir eine Structure-Proxyklasse bauen, welche für die Attributzugriffe die Werte an die richtige Speicherstelle schreibt und von dieser liest.

Re: ctypes union als rückgabewert

Verfasst: Freitag 28. Juni 2013, 11:09
von BlackJack
Vielleicht hilft die `ctypes`-Dokumentation hier weiter: Structure/union alignment and byte order