Cast to different C struct unsafe pointer in Swift

南笙酒味 提交于 2019-12-19 10:05:14

问题


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?


回答1:


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.




回答2:


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:


Swift 5 obsolete the withUnsafeBytes(UnsafePointer<sockaddr>), so below is what I'm doing with Swift 5:

        var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
    return withUnsafeBytes { (p: UnsafeRawBufferPointer) -> String? in
        let addr = p.baseAddress?.assumingMemoryBound(to: sockaddr.self)
        guard getnameinfo(addr, socklen_t(self.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
            return nil
        }
        return String(cString: hostname)
    }


来源:https://stackoverflow.com/questions/39184544/cast-to-different-c-struct-unsafe-pointer-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!