1

I'd like to create an extension for UnsafeMutablePointer that only affects UnsafeMutablePointer<UInt8>...

I understand that these instructions are pertinent, but I'm not sure how:

When you extend a generic type, you do not provide a type parameter list as part of the extension’s definition. Instead, the type parameter list from the original type definition is available within the body of the extension, and the original type parameter names are used to refer to the type parameters from the original definition.

Basically, I'm trying to use this method:

func toSwift(length: Int) -> [Int] {
    var retVal : [Int] = []
    for i in 0..<length {
        retVal.append(Int(self[i]))
    }
    return retVal
}

to act on self without the UnsafeMutablePointer<UInt8> as a parameter... is this possible?

Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
  • How are you getting an `Int` from the `UnsafeMutablePointer`? Using `memory`? – JAL Jun 22 '16 at 20:45
  • @JAL that method's signature, working, is really `func toSwiftFromData(data:UnsafeMutablePointer, length: Int) -> [Int] {` and then where you see `self` that's `data`... does that answer the question? – Dan Rosenstark Jun 22 '16 at 20:58

2 Answers2

5

Swift 3.1 Update

As of Swift 3.1 (available with Xcode 8.3 beta), concrete same-type requirements are now supported. You can now just say:

extension UnsafeMutablePointer where Pointee == UInt8 {
    func asArray(withLength length: Int) -> [Int] {
        return UnsafeBufferPointer(start: self, count: length).map(Int.init)
    }
}

Pre Swift 3.1

You can do this – although it's not particularly nice. You'll have to create a new protocol in order to 'tag' the UInt8 type, and then constrain your extension to that protocol. It also doesn't allow you to easily specify that the Int(...) initialiser can take a _UInt8Type input – you have to implement a hacky 'shadow' method to do that.

protocol _UInt8Type {
    func _asInt() -> Int
}
extension UInt8 : _UInt8Type {
    func _asInt() -> Int {
        return Int(self)
    }
}

// Change 'Pointee' to 'Memory' for Swift 2
extension UnsafeMutablePointer where Pointee : _UInt8Type {
    func asArray(withLength length:Int) -> [Int] {
        return UnsafeBufferPointer(start: self, count: length).map{$0._asInt()}
    }
}

All in all I'd prefer to keep this fully generic and go with @AMomchilov's solution. I'm only really adding this for the sake of completion.

Although it's worth noting that having concrete same-type requirements for extensions (extension Type where Generic == SomeType) has been proposed as a part of the Swift Generics Manifesto – so hopefully this will be possible in a future version of Swift.

Community
  • 1
  • 1
Hamish
  • 78,605
  • 19
  • 187
  • 280
  • this is awesome and I'll probably change this to best answer in a while. Seems a bit hacky, though... I wonder if this will break at some point. But... great and thanks for providing a working solution! – Dan Rosenstark Jun 22 '16 at 23:35
  • 1
    @DanRosenstark While it most definitely does look hacky, it's actually taking advantage of some fundamental Swift concepts (protocols & extending existing types with the 'new' functionality that the protocol defines) – therefore I don't see this ever breaking in a future version of Swift (I can't immediately think of any evolution proposals that would affect it either). Although that being said, if this is production code then I would consider making the `_UIntType` protocol `private` (which would also make the extension private). – Hamish Jun 23 '16 at 07:19
  • 1
    Always bear in mind that Swift is still evolving, and there's some neat things that have been listed as a part of the [Generics Manifesto](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md), as already said, concrete same-type requirements for extensions would make this a breeze to implement. Although there isn't yet a formal evolution proposal for it, it looks like something that'll definitely be coming in a future version of Swift. – Hamish Jun 23 '16 at 07:23
  • 1
    There's also a proposal about [improving the generic capabilities of integers](https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md) – which could also be helpful for these kind of operations :) – Hamish Jun 23 '16 at 07:25
  • Thanks. Really fascinating. When I look at the top protocol extension, my first thought is that I have no idea what I'm looking at, but in fact it's actually really clear ("UInt8 now conforms to this one-method protocol, and here is the method body for this one-method which returns Int")! This is pretty amazing, thanks again. – Dan Rosenstark Jun 23 '16 at 13:35
  • @DanRosenstark Happy to help :) – Hamish Jun 23 '16 at 13:40
2

Currently, you can only do this for protocols, not particular classes or structs. You can make a dummy protocol and extend your class/struct with it, as with originaluser2's answer.

However, I don't see a reason not to keep this code generic. What do you think of this?

extension UnsafeMutablePointer {
    func toArray(withLength length: UInt) -> [Memory] { //Change "Memory" to "Pointee" in Swift 3
        return Array(UnsafeBufferPointer(start: self, count: Int(length)))
    }
}
Community
  • 1
  • 1
Alexander
  • 59,041
  • 12
  • 98
  • 151
  • That's amazing. So the `[Memory]` is typed? – Dan Rosenstark Jun 22 '16 at 20:53
  • @DanRosenstark What do you mean? – Alexander Jun 22 '16 at 20:53
  • 1) I mean the array returned will be [UInt8]? 2) what does `Memory` mean, or perhaps a link to this keyword? Thank you! – Dan Rosenstark Jun 22 '16 at 20:55
  • 1
    `Memory` is the generic type parameter of the struct `UnsafePointer`. When you say something like `UnsafePointer`, you're saying "an `UnsafePointer`, for which the generic type parameter `Memory` is specialised to represent `UInt8`. I know it's called `Memory` from https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_UnsafePointer_Structure/index.html (or `Pointee` in Swift 3 https://developer.apple.com/reference/swift/unsafepointer) – Alexander Jun 22 '16 at 21:00
  • 1
    The quote in your question even mentions it: "Instead, the type parameter list from the original type definition is available within the body of the extension, and the original type parameter names are used to refer to the type parameters from the original definition" – Alexander Jun 22 '16 at 21:01
  • Awesome, thanks for explaining all of this! So I could return any type I want, like `[Int]`, too, I suppose? – Dan Rosenstark Jun 22 '16 at 21:08
  • 2
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/115346/discussion-between-amomchilov-and-dan-rosenstark). – Alexander Jun 22 '16 at 21:09
  • Hey there, hope you don't mind but I'm changing the accepted answer to the other one. I really do appreciate the discussion and wish you best of luck here on SO. – Dan Rosenstark Jun 23 '16 at 13:36