问题
I want to call the Posix socket functions socket
and bind
from Swift. socket
is pretty easy—it takes Int32
s, 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
andvar sockAddress: sockaddr_in
are not needed.The
memset()
is not necessary becausesockaddr_in()
initializes all struct members to zero.The Swift equivalent of the C(This "problem" does not exist anymore. For structs imported from C,sizeof
isstride
(which includes a possible struct padding), notsize
(which does not include the struct padding).stride
andsize
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