在 Swift 中转换为不同的 C 结构不安全指针
Cast to different C struct unsafe pointer in Swift
我想从 Swift 调用 Posix 套接字函数 socket
和 bind
。 socket
非常简单——需要 Int32
s,但是 bind
导致了问题,因为我有一个 sockaddr_in
指针,但它需要一个 sockaddr
指针.在 C 中,这将是一个转换,例如:
bind(sock, (struct sockaddr *)&sockAddress, sizeof(sockAddress))
这是 Swift 中的一次尝试:
let sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
var sockAddress = sockaddr_in()
bind(sock, &sockAddress, UInt32(MemoryLayout<sockaddr_in>.size))
bind
行编译失败:无法将类型 'sockaddr_in' 的值转换为预期的参数类型 'sockaddr'
如何投射指针?
你可以这样写:
withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
sockaddrInPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) {sockaddrPtr in
bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
}
}
或者有人建议这可能更好:
withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
let sockaddrPtr = UnsafeRawPointer(sockaddrInPtr).assumingMemoryBound(to: sockaddr.self)
bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
}
This article 可能会有一些帮助。
(更新)
如 中所述,现在 MemoryLayout<T>.stride
和 MemoryLayout<T>.size
return 与 C 的 sizeof
一致的相同值,其中 T 是导入的 C 结构。我会在这里保留我的 stride
版本的答案,但现在 "required" 在这种情况下不是这样。
在 Swift 3 你必须 "rebind" 指针
(比较 SE-0107 UnsafeRawPointer API):
let sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
var sockAddress = sockaddr_in()
let result = withUnsafePointer(to: &sockAddress) {
[=10=].withMemoryRebound(to: sockaddr.self, capacity: 1) {
bind(sock, [=10=], socklen_t(MemoryLayout<sockaddr_in>.stride))
}
}
备注:
不需要let sock: Int32
和var sockAddress: sockaddr_in
中的类型注释。
不需要memset()
因为sockaddr_in()
初始化
所有结构成员归零。
C sizeof
的 Swift 等效为 stride
(其中包括
可能的结构填充),而不是 size
(不包括
struct padding).(这个"problem"不存在了。
对于从 C 导入的结构,stride
和 size
具有相同的值。)
Swift 5 废弃了 withUnsafeBytes(UnsafePointer<sockaddr>)
,下面是我用 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)
}
我想从 Swift 调用 Posix 套接字函数 socket
和 bind
。 socket
非常简单——需要 Int32
s,但是 bind
导致了问题,因为我有一个 sockaddr_in
指针,但它需要一个 sockaddr
指针.在 C 中,这将是一个转换,例如:
bind(sock, (struct sockaddr *)&sockAddress, sizeof(sockAddress))
这是 Swift 中的一次尝试:
let sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
var sockAddress = sockaddr_in()
bind(sock, &sockAddress, UInt32(MemoryLayout<sockaddr_in>.size))
bind
行编译失败:无法将类型 'sockaddr_in' 的值转换为预期的参数类型 'sockaddr'
如何投射指针?
你可以这样写:
withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
sockaddrInPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) {sockaddrPtr in
bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
}
}
或者有人建议这可能更好:
withUnsafePointer(to: &sockAddress) {sockaddrInPtr in
let sockaddrPtr = UnsafeRawPointer(sockaddrInPtr).assumingMemoryBound(to: sockaddr.self)
bind(sock, sockaddrPtr, UInt32(MemoryLayout<sockaddr_in>.stride))
}
This article 可能会有一些帮助。
(更新)
如 MemoryLayout<T>.stride
和 MemoryLayout<T>.size
return 与 C 的 sizeof
一致的相同值,其中 T 是导入的 C 结构。我会在这里保留我的 stride
版本的答案,但现在 "required" 在这种情况下不是这样。
在 Swift 3 你必须 "rebind" 指针 (比较 SE-0107 UnsafeRawPointer API):
let sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
var sockAddress = sockaddr_in()
let result = withUnsafePointer(to: &sockAddress) {
[=10=].withMemoryRebound(to: sockaddr.self, capacity: 1) {
bind(sock, [=10=], socklen_t(MemoryLayout<sockaddr_in>.stride))
}
}
备注:
不需要
let sock: Int32
和var sockAddress: sockaddr_in
中的类型注释。不需要
memset()
因为sockaddr_in()
初始化 所有结构成员归零。C(这个"problem"不存在了。 对于从 C 导入的结构,sizeof
的 Swift 等效为stride
(其中包括 可能的结构填充),而不是size
(不包括 struct padding).stride
和size
具有相同的值。)
Swift 5 废弃了 withUnsafeBytes(UnsafePointer<sockaddr>)
,下面是我用 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)
}