Cast to different C struct unsafe pointer in Swift

2019-01-20 03:05发布

I want to call the Posix socket functions socket and bind from Swift. socket is pretty easy—it takes Int32s, but bind is causing a problem, because I have a sockaddr_in pointer, but it wants a sockaddr pointer. In C, this would be a cast, like:

bind(sock, (struct sockaddr *)&sockAddress, sizeof(sockAddress))

Here's an attempt in Swift:

let sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
var sockAddress = sockaddr_in()        
bind(sock, &sockAddress, UInt32(MemoryLayout<sockaddr_in>.size))

The bind line fails to compile with: cannot convert value of type 'sockaddr_in' to expected argument type 'sockaddr'

How do I cast the pointer?

2条回答
forever°为你锁心
2楼-- · 2019-01-20 03:33

In Swift 3 you have to "rebind" the pointer (compare SE-0107 UnsafeRawPointer API):

let sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
var sockAddress = sockaddr_in()

let result = withUnsafePointer(to: &sockAddress) {
    $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
        bind(sock, $0, socklen_t(MemoryLayout<sockaddr_in>.stride))
    }
}

Remarks:

  • The type annotations in let sock: Int32 and var sockAddress: sockaddr_in are not needed.

  • The memset() is not necessary because sockaddr_in() initializes all struct members to zero.

  • The Swift equivalent of the C sizeof is stride (which includes a possible struct padding), not size (which does not include the struct padding). (This "problem" does not exist anymore. For structs imported from C, stride and size have the same value.)

查看更多
淡お忘
3楼-- · 2019-01-20 03:34

You can write something like this:

withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
    sockaddrInPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) {sockaddrPtr in
        bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
    }
}

Or someone suggests this may be better:

withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
    let sockaddrPtr = UnsafeRawPointer(sockaddrInPtr).assumingMemoryBound(to: sockaddr.self)
    bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
}

This article may be some help.


(UPDATE) As described in the link shown by Martin R, now MemoryLayout<T>.stride and MemoryLayout<T>.size return the same value which is consistent with C's sizeof, where T is an imported C-struct. I'll keep my stride version of answer here, but that is not something "required" in this case now.

查看更多
登录 后发表回答