Swift converte uint64_t de C diferente do que usa seu próprio tipo UInt64

Estou no processo de portar um aplicativo de (Objective-) C para Swift, mas tenho que usar uma estrutura de terceiros escrita em C. Há algumas incompatibilidades, como typedefs, que são interpretadas como Int, mas precisam ser passadas para o diretório funções da estrutura como UInts ou similares. Portanto, para evitar operações constantes de conversão em todo o aplicativo Swift, decidi transferir os arquivos de cabeçalho C para o Swift, tendo todos os tipos necessários para que eles estivessem em um só lugar.

Consegui transferir quase tudo e superei muitos obstáculos, mas este:

O cabeçalho C define uma estrutura que contém uma variável uint64_t entre outras. Essa estrutura é usada para transferir dados para uma função de retorno de chamada como um ponteiro. A função de retorno de chamada usa um ponteiro nulo como argumento e eu tenho que convertê-lo com a operação UnsafeMutablePointer para o tipo da estrutura (ou outra estrutura do cabeçalho, se apropriado). Toda a transmissão e acesso à memória funcionam bem desde que eu use a estrutura original do cabeçalho C que foi automaticamente transformada pelo Swift na importação.

No entanto, replicar a estrutura manualmente no Swift não "ajusta o byte".

Deixe-me mostrar um exemplo reduzido dessa situação:

Dentro do arquivo CApiHeader.h, há algo como

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

Pelo meu entendimento, aqui deve ser o equivalente Swift

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

Ou o que também deve funcionar é essa notação de tupla

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

Isso funciona normalmente, mas não assim que houver um tipo de UInt64.

Ok, então o que acontece?

Ao converter o ponteiro em uma das minhas próprias implementações do Swift MyStruct, os dados do furo são alterados em 2 bytes, iniciando no campo UInt64. Então, neste exemplo, os doisarr os campos não estão na posição correta, mas dentro dos bits UInt64, que devem ter 64 em número. Por isso, parece que o campo UInt64 possui apenas 48 bits.

Isso concorda com minha observação de que, se eu substituir a variável UIn64 por essa alternativa

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

ou este

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

(ou a notação da tupla equivalente), alinha aarr campos corretamente. Mas como você pode adivinhar facilmentevar02 contém dados não utilizáveis diretamente, porque são divididos em vários intervalos de endereços. É ainda pior com a primeira alternativa, porque parece que Swift preenche a lacuna entre osreservado campo e ovar02 campo com 16 bits - os 2 bytes ausentes / deslocados que mencionei acima - mas estes não são facilmente acessíveis.

Portanto, não descobri nenhuma transformação equivalente da estrutura C no Swift.

O que acontece aqui exatamente e como o Swift transforma realmente a estrutura do cabeçalho C?

Vocês têm uma dica ou explicação ou mesmo uma solução para mim, por favor?

Atualizar

A estrutura C possui uma função API com esta assinatura:

int16_t setHan,dlers(MessageHandlerProc messageHandler);

MessageHandlerProc é o tipo de procedimento:

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

Portanto, setHandlers é um procedimento C dentro da estrutura que obtém um ponteiro para uma função de retorno de chamada. Essa função de retorno de chamada deve fornecer um argumento de um ponteiro nulo, que é convertido para, por exemplo,

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 é inteligente o suficiente para importar o messageHandlerProc com a sintaxe da convenção (c), para que o tipo de procedimento esteja diretamente disponível. Por outro lado, não é possível usar a sintaxe func padrão e transmitir bit a função de retorno de chamada messageHandler para esse tipo. Então, usei a sintaxe de fechamento para definir a função de retorno de chamada:

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

...

}

Eu converti a estrutura acima mencionada nas diferentes estruturas do meu post original.

E não! DefinindoEstatísticas como o Swift Array não funciona. Uma matriz no Swift não é equivalente a uma matriz em C, porque a matriz do Swift é um tipo estendido. Escrever e ler dele com um ponteiro causa uma exceção

Somente Tuplas são implementadas de forma nativa no Swift e você pode ir e vir com ponteiros sobre ele.

Ok ... isso funciona bem e minha função de retorno de chamada é chamada sempre que houver dados disponíveis.

Então por dentromyMessageHandler Eu quero usar os dados armazenados dentromsgArgPtr que é um ponteiro vazio e, portanto, deve ser lançadoDeviceState.

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

AcessandoEstado como:

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

Sempre que uso o pingente Swift gerado automaticamente deDeviceState tudo funciona bem. A variável time possui o carimbo de data e hora do Unix e as seguintes estatísticas (acessíveis com sintaxe de tupla !!!) estão onde pertencem.

Usar minha estrutura implementada manualmente, no entanto, resulta em um valor de carimbo de data / hora completamente sem sentido e os campos de estatísticas são deslocados para a esquerda (na direçãoTempo campo - é provavelmente por isso que o valor do carimbo de data / hora é inútil, pois contém bits do "array" de estatísticas). Nos dois últimos campos de estatísticas, recebo valores decompositeValueOld e a primeiraeixo campo - com todo o transbordamento, é claro.

Enquanto eu estiver disposto a sacrificar oTempo valor e altere a variável UInt64 por uma tupla de dois tipos de UInt32 ou alterando-a para um tipo de UInt32 e adicionando uma variável auxiliar do tipo UInt16 imediatamente antesTempoEu recebo umEstatísticas "matriz" com alinhamento correto.

Tenha um bom dia! :-)

Martin

questionAnswers(2)

yourAnswerToTheQuestion