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 passierWenn 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?
AktualisiereDas 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