3

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?

Rob N
  • 15,024
  • 17
  • 92
  • 165

3 Answers3

8

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.

Community
  • 1
  • 1
OOPer
  • 47,149
  • 6
  • 107
  • 142
  • OOPs, had not seen your answer :) – Martin R Aug 27 '16 at 19:00
  • [This article](https://forums.developer.apple.com/thread/60350#171911) suggests that `UnsafeRawPointer` works in some cases. But I'm not sure which is the better in this case. Any further info are welcome. – OOPer Aug 27 '16 at 19:06
  • According to https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20160815/002974.html, both are correct, with a slight favor of "withMemoryRebound", as I understand it. – Martin R Aug 27 '16 at 19:09
  • @MartinR, thanks. I could not be sure, with a slight glance to the article. I'll read it again taking more time. – OOPer Aug 27 '16 at 19:14
  • Here http://stackoverflow.com/a/33177600/1187415 is a case where "size" did not work, only "stride". It *is* low-level ... – Martin R Aug 27 '16 at 20:16
  • @MartinR, thanks. It seems we should always follow your third remark. I'll update my answer. – OOPer Aug 27 '16 at 20:21
  • I just noticed (http://stackoverflow.com/a/39302927/1187415) that in Swift 3, size == stride for structs imported from C. So that problem does not exist anymore. – Martin R Sep 03 '16 at 03:38
  • Started to write a comment regarding casting with double pointers, but I figured it would make more sense in a separate thread, if someone is interested in offering some help: https://stackoverflow.com/questions/51138411/cast-c-struct-double-pointer-to-a-swift-struct-unsafe-pointer – agirault Jul 02 '18 at 14:33
4

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.)

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • If you try to use `sizeof`, Xcode recommends replacing it with `size`. – Rob N Aug 27 '16 at 20:07
  • @RobN: Then file a bug report for Xcode :) In C, sizeof is the size of the struct *including* a possible padding at the end. In Swift, stride includes the struct padding, and size doesn't. – Martin R Aug 27 '16 at 20:08
  • Ha. :) Well, I'll let someone else do that, since I don't know it's wrong. I did fix the question per your other remarks, to remove the noise code. Thanks! – Rob N Aug 27 '16 at 20:09
  • @RobN: It makes no difference if the struct has no padding at the end. With stride you are on the safe side. Here http://stackoverflow.com/a/33177600/1187415 is a (perhaps exotic) case where using size did not work. – Martin R Aug 27 '16 at 20:12
0

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)
    }
Chinh Nguyen
  • 583
  • 1
  • 7
  • 14