Swift конвертирует uint64_t в C, отличный от своего собственного типа UInt64

Я нахожусь в процессе переноса приложения из (Objective-) C в Swift, но должен использовать стороннюю платформу, написанную на C. Есть несколько несовместимостей, таких как typedef, которые интерпретируются как Int, но должны быть переданы в функции фреймворка как UInts или тому подобное. Поэтому, чтобы избежать постоянных операций приведения во всем приложении Swift, я решил перенести заголовочные файлы C в Swift, имея все типы, так как мне нужно, чтобы они были в одном месте.

Я смог перенести почти все и преодолел множество препятствий, но вот этот:

Заголовок C определяет структуру, которая среди прочего содержит переменную uint64_t. Эта структура используется для передачи данных в функцию обратного вызова в качестве указателя. Функция обратного вызова принимает указатель void в качестве аргумента, и я должен привести его с помощью операции UnsafeMutablePointer к типу структуры (или другой структуре заголовка, если необходимо). Все приведение и доступ к памяти работают нормально, если я использую исходную структуру из заголовка C, которая автоматически преобразуется Swift при импорте.

Однако репликация структуры вручную в Swift не "вписывается в байты".

Позвольте мне показать вам уменьшенный пример этой ситуации:

Внутри файла CApiHeader.h есть что-то вроде

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

Насколько я понимаю, здесь должен быть эквивалент Swift

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

Или что также должно работать, это запись кортежа

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

Это работает нормально, но не тогда, когда есть тип UInt64.

Хорошо, что происходит?

Приведя указатель на одну из моих собственных реализаций Swift MyStruct, данные дырок сдвигаются на 2 байта, начиная с поля UInt64. Так что в этом примере обаприбытие поля находятся не в правильной позиции, а внутри битов UInt64, число которых должно быть 64. Таким образом, кажется, что поле UInt64 имеет только 48 бит.

Это соответствует моему наблюдению, что если я заменю переменную UIn64 этой альтернативой

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

или этот

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

(или эквивалентное обозначение кортежа) это выравниваетприбытие поля правильно. Но, как вы можете легко догадаться,var02 содержит данные, которые нельзя использовать напрямую, поскольку они разбиты на несколько диапазонов адресов. Еще хуже с первой альтернативой, потому что это швы, что Swift заполняет промежуток междузарезервированный поле иvar02 поле с 16 битами - отсутствующие / сдвинутые 2 байта, о которых я упоминал выше - но они не легко доступны.

Так что я не нашел никакого эквивалентного преобразования структуры C в Swift.

Что именно здесь происходит и как Swift на самом деле преобразует структуру из заголовка C?

Ребята, у вас есть подсказка, объяснение или решение для меня, пожалуйста?

Обновить

Фреймворк C имеет функцию API с этой сигнатурой:

int16_t setHan,dlers(MessageHandlerProc messageHandler);

MessageHandlerProc - это тип процедуры:

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

Так что setHandlers - это процедура на C внутри фреймворка, которая получает указатель на функцию обратного вызова. Эта функция обратного вызова должна предоставлять аргумент пустого указателя, который приводится, например, к.

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 достаточно умен, чтобы импортировать messageHandlerProc с синтаксисом соглашения (c), поэтому тип процедуры доступен напрямую. С другой стороны, невозможно использовать стандартный синтаксис func и передать функцию обратного вызова my messageHandler для этого типа. Поэтому я использовал синтаксис замыкания для определения функции обратного вызова:

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

...

}

Я преобразовал вышеупомянутую структуру в различные структуры моего оригинального сообщения.

И нет! определяющийстатистика как Swift Array не работает. Массив в Swift не эквивалентен массиву в C, поскольку массив Swift является расширенным типом. Запись и чтение из него с указателем вызывает исключение

Только кортежи изначально реализованы в Swift, и вы можете бегать туда-сюда с указателями над ним.

Хорошо ... все работает отлично, и моя функция обратного вызова вызывается всякий раз, когда доступны данные.

Так внутриmyMessageHandler Я хочу использовать хранящиеся данные внутриmsgArgPtr который является пустым указателем и поэтому должен быть приведен вDeviceState.

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

Доступ кгосударство это как:

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

Всякий раз, когда я использую автоматически сгенерированный кулон SwiftDeviceState все работает хорошо. Переменная времени имеет метку времени Unix, и следующие статистические данные (доступные с синтаксисом кортежа !!!) - все, где они принадлежат.

Однако использование моей структуры, реализованной вручную, приводит к совершенно бессмысленному значению метки времени, а поля статистики сдвигаются влево (в направлениивремя поле - возможно, поэтому значение метки времени бесполезно, потому что оно содержит биты из статистического «массива»). Таким образом, в последних двух полях статистики я получаю значения отcompoundValueOld и первыйось поле - со всем переполнением конечно.

Пока я готов пожертвоватьвремя установите значение переменной UInt64 и измените ее, используя кортеж двух типов UInt32 или изменив его на тип UInt32 и добавив вспомогательную переменную типа UInt16 непосредственно передвремяЯ получаюстатистика «массив» с правильным выравниванием.

Хорошего дня! :-)

Мартин

Ответы на вопрос(2)

Ваш ответ на вопрос