[swift-users] Canonical way to cast C structs

Andrew Trick atrick at apple.com
Wed Sep 14 01:34:57 CDT 2016


> On Sep 12, 2016, at 12:39 PM, Bouke Haarsma via swift-users <swift-users at swift.org> wrote:
> 
> Sorry for all the pings, but it appears that the code below doesn’t work after all;
> 
>> fatal error: can't unsafeBitCast between types of different sizes
> 
> So the question remains on how to perform the casts using Swift?
> 
>> Bouke

Hi Bouke,

Please see this migration guide:
https://swift.org/migration-guide/se-0107-migrate.html <https://swift.org/migration-guide/se-0107-migrate.html>

It explains a few things that are not self-explanatory and includes some helper code for dealing with the socket API.

Given how your code is structured, I would start with a raw pointer, then replace all the ‘withMemoryRebound’ calls to ‘bindMemory’, and you should be fine:

var rawSockAddr = UnsafeRawPointer(CFDataGetBytePtr(data))

switch …
case …:
  let ipv4 = rawSockAddr.bindMemory(to: sockaddr_in.self, capacity: 1)

You might also be able to avoid CFData and use Swift’s Data directly, but any Swifty API you use will encourage you to restructure your code so that pointer access is confined to a closure, like Data.withUnsafeBytes, or UnsafePointer.withMemoryRebound(to:capacity:). You should never return the pointer argument from these closure. The closure taking APIs are designed to keep your data alive while you access it and make sure you’re not mixing pointers of different types to the same memory.

-Andy

>> On 12 sep. 2016, at 21:37, Bouke Haarsma <bouke at haarsma.eu <mailto:bouke at haarsma.eu>> wrote:
>> 
>> 
>> Sorry, missed the first line when copying:
>> 
>> let generic = unsafeBitCast(CFDataGetBytePtr(data), to: sockaddr.self)
>> switch generic.sa_family {
>> case sa_family_t(AF_INET):
>>     let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
>>     //...
>> case sa_family_t(AF_INET6):
>>     let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
>>     //...
>> default:
>>     //...
>> }
>> 
>>>> Bouke
>> 
>>> On 12 sep. 2016, at 21:35, Bouke Haarsma <bouke at haarsma.eu <mailto:bouke at haarsma.eu>> wrote:
>>> 
>>> Ah the missing part of the puzzle appears to be unsafeBitCast(:to:), so the Swift version becomes this:
>>> 
>>> switch generic.sa_family {
>>> case sa_family_t(AF_INET):
>>>     let ipv4 = unsafeBitCast(generic, to: sockaddr_in.self)
>>>     //...
>>> case sa_family_t(AF_INET6):
>>>     let ipv6 = unsafeBitCast(generic, to: sockaddr_in6.self)
>>>     //...
>>> default:
>>>     //...
>>> }
>>> 
>>>>>> Bouke
>>> 
>>>> On 12 sep. 2016, at 21:25, Bouke Haarsma <bouke at haarsma.eu <mailto:bouke at haarsma.eu>> wrote:
>>>> 
>>>> Hi all,
>>>> 
>>>> Inside my CFSocketCallBack a pointer to a sockaddr is provided wrapped in a CFData structure. The sockaddr could be either a sockaddr_in (IPv4) or sockaddr_in6 (IPv6) struct. In order to discover which struct you’re dealing with, the attribute sa_family can be inspected. In C this would look something like this:
>>>> 
>>>> struct sockaddr_storage sa;
>>>> 
>>>> switch (((sockaddr*)&sa)->sa_family)
>>>> {
>>>>     case AF_INET:
>>>>         inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), ...);
>>>>         break;
>>>>     case AF_INET6:
>>>>         inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), ...);
>>>>         break;
>>>> }
>>>> (from: http://stackoverflow.com/a/13167913 <http://stackoverflow.com/a/13167913>)
>>>> 
>>>> Wheras to do this in Swift 3, the only way I found thus far is this:
>>>> 
>>>> var generic = CFDataGetBytePtr(data).withMemoryRebound(to: sockaddr.self, capacity: 1) {
>>>>     return $0.pointee
>>>> }
>>>> switch generic.sa_family {
>>>> case sa_family_t(AF_INET):
>>>>     let ipv4 = withUnsafePointer(to: &generic) {
>>>>         $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
>>>>             $0.pointee
>>>>         }
>>>>     }
>>>>     //...
>>>> case sa_family_t(AF_INET6):
>>>>     let ipv6 = withUnsafePointer(to: &generic) {
>>>>         $0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) {
>>>>             $0.pointee
>>>>         }
>>>>     }
>>>>     //...
>>>> default:
>>>>     //…
>>>> }
>>>> 
>>>> This looks very ugly with all the closures and ``withMemoryRebound``. Is there a better way to do this? I think there should be something more “Swifty”.
>>>> 
>>>>>>>> Bouke
>>> 
>> 
> 
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20160913/02a94a53/attachment.html>


More information about the swift-users mailing list