Swift konvertiert Cs uint64_t anders als es seinen eigenen UInt64-Typ verwendet

Ich bin dabei, eine Anwendung von (Objective-) C nach Swift zu portieren, muss jedoch ein in C geschriebenes Framework eines Drittanbieters verwenden. Es gibt einige Inkompatibilitäten wie typedefs, die als Int interpretiert werden, aber an übergeben werden müssen Das Framework fungiert als UInts oder dergleichen. Um konstante Casting-Vorgänge während der gesamten Swift-Anwendung zu vermeiden, habe ich beschlossen, die C-Header-Dateien an Swift zu übertragen, wobei alle Typen an einem Ort sein müssen.

Ich konnte fast alles übertragen und habe viele Hürden genommen, aber diese:

Der C-Header definiert eine Struktur, die unter anderem eine Variable uint64_t enthält. Diese Struktur wird verwendet, um Daten als Zeiger an eine Rückruffunktion zu übergeben. Die Rückruffunktion nimmt einen void-Zeiger als Argument und ich muss ihn mit der UnsafeMutablePointer-Operation auf den Typ der Struktur (oder eine andere Struktur des Headers, falls zutreffend) umwandeln. Das Casting und der Speicherzugriff funktionieren einwandfrei, solange ich die ursprüngliche Struktur aus dem C-Header verwende, die beim Import von Swift automatisch transformiert wurde.

Das manuelle Replizieren der Struktur in Swift führt jedoch nicht zu einer "Byte-Anpassung".

Lassen Sie mich Ihnen ein Beispiel für diese Situation zeigen:

In der CApiHeader.h-Datei gibt es so etwas wie

typedef struct{
  uint32_t var01;
  uint64_t var02;
  uint8_t arr[2];
}MyStruct, *MyStructPtr;

ach meinem Verständnis sollte dies hier das Swift-Äquivalent sei

struct MyStruct{
  var01: UInt32
  var02: UInt64
  arr: (UInt8, UInt8)
}

Oder was auch funktionieren sollte ist diese Tupelnotation

typealias MyStruct = (
  var01: UInt32,
  var02: UInt64,
  arr: (UInt8, UInt8)
)

Dies funktioniert normal, aber nicht, sobald es einen UInt64-Typ gibt.

lso, was passier

Wenn Sie den Zeiger auf eine meiner eigenen Swift MyStruct-Implementierungen setzen, werden die Lochdaten ab dem Feld UInt64 um 2 Byte verschoben. Also in diesem Beispiel die beiden arr -Felder befinden sich nicht an der richtigen Position, aber innerhalb der UInt64-Bits sollte die Zahl 64 sein. Es scheint also, dass das UInt64-Feld nur 48 Bit hat.

Dies entspricht meiner Beobachtung, dass, wenn ich die UIn64-Variable durch diese Alternative ersetze

struct MyStruct{
  var01: UInt32
  reserved: UInt16
  var02: UInt32
  arr: (UInt8, UInt8)
}

oder diese

struct MyStruct{
  var01: UInt32
  var02: (UInt32, UInt32)
  arr: (UInt8, UInt8)
}

(oder die entsprechende Tupelnotation) richtet das @ a arr Felder richtig. Aber wie du leicht erraten kannst var02 enthält keine direkt verwendbaren Daten, da diese auf mehrere Adressbereiche aufgeteilt sind. Bei der ersten Alternative ist es sogar noch schlimmer, denn Swift füllt die Lücke zwischen demreservier Feld und das var02 -Feld mit 16 Bits - die fehlenden / verschobenen 2 Bytes, die ich oben erwähnt habe - aber diese sind nicht leicht zugänglich.

So habe ich keine äquivalente Transformation der C-Struktur in Swift herausgefunden.

Was passiert hier genau und wie transformiert Swift die Struktur tatsächlich aus dem C-Header?

Hast ihr bitte einen Tipp, eine Erklärung oder sogar eine Lösung für mich?

Aktualisiere

Das C-Framework verfügt über eine API-Funktion mit dieser Signatur:

int16_t setHan,dlers(MessageHandlerProc messageHandler);

MessageHandlerProc ist Prozedurtyp:

typedef void (*messageHandlerProc)(unsigned int id, unsigned int messageType, void *messageArgument);

So setHandlers ist eine C-Prozedur innerhalb des Frameworks, die einen Zeiger auf eine Rückruffunktion erhält. Diese Rückruffunktion muss ein Argument eines ungültigen Zeigers bereitstellen, das auf beispielsweise @ umgewandelt wir

typedef struct {
    uint16_t        revision;
    uint16_t        client;
    uint16_t        cmd;
    int16_t         parameter;
    int32_t         value;
    uint64_t        time;
    uint8_t         stats[8];
    uint16_t        compoundValueOld;
    int16_t         axis[6];
    uint16_t        address;
    uint32_t        compoundValueNew;
} DeviceState, *DeviceStatePtr;

Swift ist intelligent genug, um den messageHandlerProc mit der Konvention (c) -Syntax zu importieren, sodass der Prozedurtyp direkt verfügbar ist. Auf der anderen Seite ist es nicht möglich, die Standardfunktionssyntax zu verwenden und meine messageHandler-Rückruffunktion auf diesen Typ zu übertragen. Also habe ich die Schließungssyntax verwendet, um die Rückruffunktion zu definieren:

let myMessageHandler : MessageHandlerProc = { (deviceID : UInt32, msgType : UInt32, var msgArgPtr : UnsafeMutablePointer<Void>) -> Void in

...

}

Ich habe die oben genannte Struktur in die verschiedenen Strukturen meines ursprünglichen Beitrags konvertiert.

Und nein DefinierenStatistike als Swift Array funktioniert nicht. Ein Array in Swift entspricht nicht einem Array in C, da Swifts Array ein erweiterter Typ ist. Das Schreiben und Lesen mit einem Zeiger löst eine Ausnahme aus

Only Tuples sind nativ in Swift implementiert und Sie können mit Zeigern darüber hin und her laufen.

Okay ... das funktioniert einwandfrei und meine Rückruffunktion wird aufgerufen, sobald Daten verfügbar sind.

Also drinnen myMessageHandler Ich möchte die gespeicherten Daten in @ verwend msgArgPtr ist ein ungültiger Zeiger und muss daher in @ umgewandelt werd DeviceState.

let state = (UnsafeMutablePointer<MyDeviceState>(msgArgPtr)).memory

AccessingZustan es mag:

...
print(state.time)
print(state.stats.0)
...

Wenn ich den automatisch generierten Swift Pendant von @ benut DeviceState es funktioniert alles gut. Die Zeitvariable hat den Unix-Zeitstempel und die folgenden Statistiken (zugänglich mit Tupelsyntax !!!) befinden sich dort, wo sie hingehören.

Mit meiner manuell implementierten Struktur ergibt sich jedoch ein völlig sinnloser Zeitstempelwert, und die Statistikfelder werden nach links verschoben (in Richtung @Zei field - das ist wahrscheinlich der Grund, warum der Zeitstempelwert unbrauchbar ist, weil er Bits aus dem stats "array" enthält. In den letzten beiden Feldern erhalte ich Werte von compoundValueOld und das ersteAchs Feld - mit all dem Überlaufen natürlich.

olange ich bereit bin, das @ zu opfeZei Wert und ändern Sie die UInt64-Variable entweder durch ein Tupel von zwei UInt32-Typen oder durch Ändern in einen UInt32-Typ und Hinzufügen einer Hilfsvariablen des Typs UInt16 direkt vorZei, Ich erhalte einStatistike "array" mit korrekter Ausrichtung.

Einen schönen Tag noch! : -)

Martin

Antworten auf die Frage(4)

Ihre Antwort auf die Frage