斯威夫特c转换的uint64_t中不同于它使用自己的UINT64类型 [英] Swift converts C's uint64_t different than it uses its own UInt64 type

查看:790
本文介绍了斯威夫特c转换的uint64_t中不同于它使用自己的UINT64类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在从移植(Objective-)C斯威夫特应用程序的过程,但必须使用一个用C编写的第三方框架有像他这样间preTED为int的typedef一对夫妇的不兼容问题但必须被传递到框架的功能UInts或类似物。因此,为了避免不断铸造业务遍及整个斯威夫特应用程序,我决定将C头文件斯威夫特,有各种类型的,因为我我需要他们在一个地方。

I am in the process of porting an application from (Objective-)C to Swift but have to use a third-party framework written in C. There are a couple of incompatibilities like typedefs that are interpreted as Int but have to be passed to the framework's functions as UInts or the like. So to avoid constant casting operations throughout the entire Swift application I decided to transfer the C header files to Swift, having all types as I I need them to be in one place.

我能几乎所有转让和克服了很多障碍,但是这一个:

I was able to transfer nearly everything and have overcome a lot of hurdles, but this one:

C头定义包含在其他一uint64_t中的变量一个结构。这个结构用于数据作为指针转移到的回调函数。回调函数接受一个空指针作为参数,我必须与UnsafeMutablePointer操作它转换到结构(或如果合适头的另一结构)的类型。所有铸件和内存访问正常工作,只要我使用的C头被自动斯威夫特进口转变原有的结构。

The C header defines a struct which contains a uint64_t variable among others. This struct is used to transfer data to a callback function as a pointer. The callback function takes a void pointer as argument and I have to cast it with the UnsafeMutablePointer operation to the type of the struct (or another struct of the header if appropriate). All the casting and memory-accessing works fine as long as I use the original struct from the C header that was automatically transformed by Swift on import.

在复制雨燕手动结构不字节适合但是。

Replicating the struct manually in Swift does not "byte-fit" however.

让我告诉你这种情况减少的例子:

Let me show you a reduced example of this situation:

在CApiHeader.h文件有类似

Inside the CApiHeader.h file there is something like

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

从我的理解这这里应该是雨燕等同

From my understanding this here should be the Swift equivalent

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

还是什么也应努力为这个元组标记

Or what should also work is this tuple notation

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

这正常工作,但不是只要有一个UINT64类型。

This works normally, but not as soon as there is an UInt64 type.

铸造指针以自己夫特MYSTRUCT实现空穴数据由2字节偏移,开始于UINT64字段之一。所以在这个例子中两个的改编字段未在正确的位置,但UINT64位内,即应该在数64。因此,接缝的UINT64场只有48位。

Casting the pointer to one of my own Swift MyStruct implementations the hole data is shifted by 2 bytes, starting at the UInt64 field. So in this example the both arr fields are not at the correct position, but inside the UInt64 bits, that should be 64 in number. So it seams that the UInt64 field has only 48 bits.

这符合我的观察,如果我用这种替代替换UIn64变量

This accords to my observation that if I replace the UIn64 variable with this alternative

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

或这个

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

(或等效的元组符号)是正确对齐的改编字段。
但你可以轻易猜出的 VAR02 包含不直接可用的数据,因为它拆分为多个地址范围。它甚至与第一种选择更糟糕,因为它接缝的斯威夫特填补了保留字段,并在 VAR02 字段之间的差距有16​​位 - 缺少/移2个字节我上面提到的 - 但这些都不方便

(or the equivalent tuple notation) it aligns the arr fields correctly. But as you can easily guess var02 contains not directly usable data, because it is split over multiple address ranges. It is even worse with the first alternative, because it seams that Swift fills up the gap between the reserved field and the var02 field with 16 bits - the missing / shifted 2 bytes I mentioned above - but these are not easily accessible.

所以我还没有想出斯威夫特的C结构的任何等效变换。

So I haven't figured out any equivalent transformation of the C struct in Swift.

究竟发生在这里和斯威夫特如何做从C头变换结构实际上是?

What happens here exactly and how does Swift transforms the struct from the C header actually?

你们是否有暗示或对我的解释甚至解决方案,请?

Do you guys have a hint or an explanation or even a solution for me, please?

的C框架有与此签名API函数:

The C framework has an API function with this signature:

int16_t setHandlers(MessageHandlerProc messageHandler);

MessageHandlerProc是过程类型:

MessageHandlerProc is procedure type:

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

所以setHandlers是获取一个指向回调函数的框架内一个C程序。此回调函数必须提供一个void指针,这被浇铸到例如的参数。

So setHandlers is a C procedure inside the framework that gets a pointer to a callback function. This callback function has to provide an argument of a void Pointer, that gets casted to e.g.

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;

雨燕是足够聪明的导入与公约(c)语法messageHandlerProc,所以程序类型是直接可用。另一方面,不可能使用标准FUNC语法和bitcast我的MessageHandler回调函数,此类型。所以我用封闭语法定义回调函数:

Swift is smart enough to import the messageHandlerProc with the convention(c) syntax, so the procedure type is directly available. On the other hand it is not possible use the standard func syntax and bitcast my messageHandler callback function to this type. So I used the closure syntax to define the callback function:

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

...

}

我转换上述结构到我原来的职位的不同结构。

I converted the above mentioned structure into the different structures of my original post.

和NO!定义的统计信息作为雨燕阵不起作用。在不斯威夫特阵列等价于C数组,因为斯威夫特的阵列是一种扩展型。写入和指针从中读取导致异常

And No! Defining stats as Swift Array does not work. An Array in Swift in not equivalent to an Array in C, because Swift's Array is a extended type. Writing to and reading from it with a pointer causes an exception

在这里输入的形象描述

只有元组在斯威夫特本地实现的,你可以在它来回跑的指针。

Only Tuples are natively implemented in Swift and you can run back and forth with pointers over it.

好吧......这所有的作品很好,我的回调函数被调用时的数据是可用的。

Okay... this works all fine and my callback function gets called whenever data is available.

所以里面的 myMessageHandler 我要使用存储的数据里面的 msgArgPtr 这是一个空指针,因此具有被丢在 DeviceState

So inside myMessageHandler I want to use the stored Data inside msgArgPtr which is a void pointer and thus has to be cast into DeviceState.

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

访问的状态它像:

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

每当我使用的自动生成的雨燕挂件 DeviceState 这一切工作得很好。时间变量具有Unix的时间戳和以下统计数据(以元组语法访问!)都属于他们。

Whenever I use the automatically generated Swift pendant of DeviceState it all works nicely. The time variable has the Unix Time Stamp and the following stats (accessible with tuple syntax!!!) are all where they belong.

使用我的手动实现结构却导致完全懵了时间戳值和统计数据字段向左偏移(向时间字段 - 这可能是为什么时间戳值是没用的,因为它包含从统计阵)位。因此,在统计的最后两个字段我从 compoundValueOld 并第一个字段值 - 与当前所有的泛滥

Using my manually implemented struct however results in a completely senseless time stamp value and the stats fields are shifted to the left (towards the time field - that's probably why the time stamp value is useless, because it contains bits from the stats "array"). So in the last two fields of stats I get values from compoundValueOld and the first axis field - with all the overflowing of course.

只要我愿意牺牲在时间值,并且由两个UInt32的类型的元组,或将其更改为UInt32的类型,并添加类型的辅助变量改变UINT64变量UINT16权利之前的时间后,我收到的统计信息阵列以正确的定位。

As long as I am willing to sacrifice the time value and change the UInt64 variable by either a tuple of two UInt32 types or by changing it to a UInt32 type and adding a auxiliary variable of the type UInt16 right before time, I receive a stats "array" with correct alignment.

有一个愉快的一天! : - )

Have a nice day! :-)

马丁

推荐答案

这是读你的更新问题,并尝试多一些之后的更新,我刚才的答复。我相信这个问题是进口的C结构,你在雨燕手动实现一之间的对准差异。这个问题可以通过使用C辅助函数来获得空指针的C结构的一个实例,截至昨日建议,然后可以转换为手动实现斯威夫特结构来解决。

This is an update to my earlier answer after reading your updated question and experimenting some more. I believe the problem is an alignment discrepancy between the imported C structure and the one you manually implemented in Swift. The problem can be solved by using a C helper function to get an instance of the C struct from void pointer as was suggested yesterday, which can then be converted to the manually implemented Swift struct.

我已经能够创建DeviceState结构的简化实物模型,看起来像

I've been able to reproduce the problem after creating an abbreviated mock-up of your DeviceState structure that looks like

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;
} APIStruct;

相应的手工制作的雨燕天然结构是:

The corresponding hand-crafted Swift native structure is:

struct MyStruct
{
    init( _apis : APIStruct)
    {
        revision = _apis.revision
        client = _apis.client  
        cmd = _apis.cmd        
        parameter = _apis.parameter
        value = _apis.value
        time = _apis.time
        stats = _apis.stats
        compoundValueOld = _apis.compoundValueOld
    }

    var     revision : UInt16
    var     client : UInt16          
    var     cmd : UInt16             
    var     parameter : Int16
    var     value : Int32
    var     time : UInt64
    var     stats : (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8);
    var     compoundValueOld : UInt16
}

在用使用不同的结构填料,从而产生一个非匹配对准可能已编译工作的C框架。我用

The C framework you are working with could have been compiled using a different struct packing, resulting in a non-matching alignment. I used

#pragma pack(2) 

在我的C code打破了斯威夫特的本地和进口C结构之间的位匹配。

in my C code to break the bit-matching between the Swift's native and imported C struct.

如果我做这样的事情。

func swiftCallBackVoid( p: UnsafeMutablePointer<Void> )
{
     ...
    let _locMS:MyStruct = (UnsafeMutablePointer<MyStruct>(p)).memory
     ...
}

在_locMS的数据从什么是用C code放置在那里一样。只有当我更改使用我的C code编译一个结构的压缩会发生此问题;如果使用默认对齐上述不安全的转换工作正常。可以如下解决这个问题:

the data in _locMS is different from what was placed there by C code. This problem only occurs if I change struct packing using a pragma in my C code; the above unsafe conversion works fine if the default alignment is used. One can solve this problem as follows:

let _locMS:MyStruct = MyStruct(_apis: (UnsafeMutablePointer<APIStruct>(p)).memory)

BTW,雨燕的方式导入C结构,阵列成员成为元组;这可以从元组表示法已经被用来访问它们中夫特的事实中可以看出。

BTW, the way Swift imports the C struct, the array members become tuples; this can be seen from the fact that tuple notation has to be used to access them in Swift.

我有一个样本X code说明这一切,我已经放在GitHub上的项目:

I have a sample Xcode project illustrating all this that I've placed on github:

https://github.com/omniprog/xcode-samples

显然,使用一个辅助C函数以获得从一个空指针APIStruct然后转换APIStruct到MYSTRUCT可以是或可以不是一种选择,这取决于结构的使用方式,它们是多么大,而位于该方法应用的性能要求。正如你所知道的,这种方法涉及到结构的一些拷贝。其他的方法,我认为,包括写斯威夫特code和第三方C框架之间的C层,学习的C结构的内存布局,并以创造性的方式访问它(可能容易折断),使用导入C结构更广泛地在你的斯威夫特code,等等...

Obviously, the approach of using a helper C function to get APIStruct from a void pointer and then converting the APIStruct to MyStruct may or may not be an option, depending on how the structures are used, how large they are, and on the performance requirements of the application. As you can tell, this approach involves some copying of the structure. Other approaches, I think, include writing a C-layer between Swift code and the 3rd party C framework, studying the memory layout of the C structure and accessing it in creative ways (may break easily), using the imported C struct more extensively in your Swift code, etc...

下面是没有不必要的复制,并与斯威夫特至C code可见所做的更改共享C和斯威夫特code之间数据的方法。用下面的方法,但是,它的当务之急是了解对象的生命周期和其他内存管理问题。一个可以创建一个类如下:

Here is a way to share data between C and Swift code without unnecessary copying and with changes made in Swift visible to C code. With the following approach, however, it's imperative to be aware of object lifetime and other memory management issues. One can create a class as follows:

// This typealias isn't really necessary, just a convenience
typealias APIStructPtr = UnsafeMutablePointer<APIStruct>

struct MyStructUnsafe
{
    init( _p : APIStructPtr )
    {
        pAPIStruct = _p
    }

    var time: UInt64 {
        get {
            return pAPIStruct.memory.time
        }
        set( newVal ) {
            pAPIStruct.memory.time = newVal
        }
    }
    var   pAPIStruct: APIStructPtr
}

然后,我们可以按如下方式使用结构:

Then we can use this structure as follows:

func swiftCallBackVoid( p: UnsafeMutablePointer<Void> )
{
   ...
   var _myUnsafe : MyStructUnsafe = MyStructUnsafe(_p: APIStructPtr(p))
   ... 
   _myUnsafe.time = 9876543210  // this change is visible in C code!
   ...
}

这篇关于斯威夫特c转换的uint64_t中不同于它使用自己的UINT64类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆