On 18 Aug 2016, at 08:28, Quinn The Eskimo! via swift-users <swift-users@swift.org> wrote:
> In my case I introduced an abstract `Address` type (basically a wrapper > around `sockaddr_storage`) and then added a method to that object which calls > a closure with the right parameters (actually, multiple such methods, > depending on whether I’m calling something like `connect` which takes an > address, or `getpeername`, which returns one). This approach concentrates > all the ugly in one place, making the rest of my BSD Sockets code much > cleaner. I’ve been revisiting this issue recently and decided to tidy up my code enough to share with others. It’s pasted in below. Bon apétit! Share and Enjoy -- Quinn "The Eskimo!" <http://www.apple.com/developer/> Apple Developer Relations, Developer Technical Support, Core OS/Hardware --------------------------------------------------------------------------- import Darwin extension sockaddr_storage { /// Calls a closure with traditional BSD Sockets address parameters. /// /// This is used to call BSD Sockets routines like `connect`, which accept their /// address as an `sa` and `saLen` pair. For example: /// /// let ss: sockaddr_storage = … /// let connectResult = ss.withSockAddr { (sa, saLen) in /// connect(fd, sa, saLen) /// } /// /// - parameter body: A closure to call with `self` referenced appropriately for calling /// BSD Sockets APIs that take an address. /// /// - throws: Any error thrown by `body`. /// /// - returns: Any result returned by `body`. func withSockAddr<ReturnType>(_ body: (_ sa: UnsafePointer<sockaddr>, _ saLen: socklen_t) throws -> ReturnType) rethrows -> ReturnType { // We need to create a mutable copy of `self` so that we can pass it to `withUnsafePointer(to:_:)`. var ss = self // Get a typed unsafe pointer to `ss`. return try withUnsafePointer(to: &ss) { // Temporarily view that as `sockaddr` while we call `body`. try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { try body($0, socklen_t(self.ss_len)) } } } /// Calls a closure such that it can return an address based on traditional BSD Sockets parameters. /// /// This is used to call BSD Sockets routines like `accept`, which return a value (the file /// descriptor) and an address via memory pointed to by `sa` and `saLen` parameters. For example: /// /// let (acceptResult, peerAddr) = sockaddr_storage.fromSockAddr { (_ sa: UnsafeMutablePointer<sockaddr>, _ saLen: inout socklen_t) in /// return accept(fd, sa, &saLen) /// } /// /// - parameter body: A closure to call with parameters appropriate for calling BSD Sockets APIs /// that return an address. /// /// - throws: Any error thrown by `body`. /// /// - returns: A tuple consistent of the result returned by `body` and an address set up by /// `body` via its `sa` and `saLen` parameters. static func fromSockAddr<ReturnType>(_ body: (_ sa: UnsafeMutablePointer<sockaddr>, _ saLen: inout socklen_t) throws -> ReturnType) rethrows -> (ReturnType, sockaddr_storage) { // We need a mutable `sockaddr_storage` so that we can pass it to `withUnsafePointer(to:_:)`. var ss = sockaddr_storage() // Similarly, we need a mutable copy of our length for the benefit of `saLen`. var saLen = socklen_t(MemoryLayout<sockaddr_storage>.size) // Get a typed unsafe pointer to `ss`. let result = try withUnsafePointer(to: &ss) { // Temporarily view that as `sockaddr` while we call `body`. try $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { try body($0, &saLen) } } return (result, ss) } /// Calls a closure with an address parameter of a user-specified type. /// /// This makes it easy to access the fields of an address as the appropriate type. For example: /// /// let sin: sockaddr_storage = … initialise with an AF_INET address … /// sin.withSockAddrType { (sin: inout sockaddr_in) in /// print(sin.sin_len) /// print(UInt16(bigEndian: sin.sin_port)) /// } /// /// In this case the closure returns void, but there may be other circumstances where it's useful /// to have a return type. /// /// - note: `body` takes an inout parameter for the sake of folks who need to take /// a pointer to elements of that parameter. We ignore any changes that the `body` /// might make to this value. Without this affordance, the following code would not /// work: /// /// let sus: sockaddr_storage = … initialise with an AF_UNIX address … /// sus.withSockAddrType { (sun: inout sockaddr_un) in /// print(sun.sun_len) /// print(String(cString: &sun.sun_path.0)) /// } /// /// - parameter body: A closure to call with `self` referenced via an arbitrary type. /// Careful with that axe, Eugene. /// /// - throws: Any error thrown by `body`. /// /// - returns: Any result returned by `body`. /// /// - precondition: `AddrType` must not be larger than `sockaddr_storage`. func withSockAddrType<AddrType, ReturnType>(_ body: (_ sax: inout AddrType) throws -> ReturnType) rethrows -> ReturnType { precondition(MemoryLayout<AddrType>.size <= MemoryLayout<sockaddr_storage>.size) // We need to create a mutable copy of `self` so that we can pass it to `withUnsafePointer(to:_:)`. var ss = self // Get a typed unsafe pointer to `ss`. return try withUnsafeMutablePointer(to: &ss) { // Temporarily view that as `AddrType` while we call `body`. try $0.withMemoryRebound(to: AddrType.self, capacity: 1) { try body(&$0.pointee) } } } /// Calls a closure such that it can return an address via a user-specified type. /// /// This is useful if you want to create an address from a specific sockaddr_xxx /// type that you initialise piecemeal. For example: /// /// let (_, sin) = sockaddr_storage.fromSockAddr { (sin: inout sockaddr_in) in /// sin.sin_family = sa_family_t(AF_INET) /// sin.sin_len = UInt8(MemoryLayout<sockaddr_in>.size) /// sin.sin_port = (12345 as in_port_t).bigEndian /// } /// /// In this case the closure returns void, but there may be other circumstances where it's useful /// to have a return type. /// /// - parameter body: A closure to call with parameters appropriate for returning an address. /// /// - throws: Any error thrown by `body`. /// /// - returns: A tuple consistent of the result returned by `body` and an address set /// up by `body` via the `sax` inout parameter. /// /// - precondition: `AddrType` must not be larger than `sockaddr_storage`. static func fromSockAddr<AddrType, ReturnType>(_ body: (_ sax: inout AddrType) throws -> ReturnType) rethrows -> (ReturnType, sockaddr_storage) { precondition(MemoryLayout<AddrType>.size <= MemoryLayout<sockaddr_storage>.size) // We need a mutable `sockaddr_storage` so that we can pass it to `withUnsafePointer(to:_:)`. var ss = sockaddr_storage() // Get a typed unsafe pointer to `ss`. let result = try withUnsafePointer(to: &ss) { // Temporarily view that as `AddrType` while we call `body`. try $0.withMemoryRebound(to: AddrType.self, capacity: 1) { try body(&$0.pointee) } } return (result, ss) } } --------------------------------------------------------------------------- _______________________________________________ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users