How to access `UInt -> UnsafePointer<P.Type> -> .pointee` without a trap?

A few questions upfront:

  • are existentials of types cached and kept alive (a pointer to a newly created instance is always the same)?
  • we should be able to restore access to that existential instance from a pointer right?

Here is what I'm trying to do, but failing at:

protocol P {
  static func foo()
}
​
struct ConcreteP: P {
  static func foo() {
    print("ConcreteP foo")
  }
}
​
let existential: P.Type = ConcreteP.self
let identifier = ObjectIdentifier(existential)
let bits = UInt(bitPattern: identifier)
​
if let pointer = UnsafePointer<P.Type>(bitPattern: bits) {
  print(pointer, String(bits, radix: 16), identifier, separator: "\n")
​
  // THIS TRAPS! :(
//  print(pointer.pointee)
}

I'm not that comfortable with the Unsafe* API family, so any help is appreciated.

I figured out that ObjectIdentifier is probably not what I wanted, but with other approach of creating the bits from an UnsafePointer leads me to a dead-end where I cannot re-create the existential from the bits as the original existential is deallocated.

If there is a good way of doing the latter, I'd appreciate the help.

If its lifetime is scoped, you can use withUnsafePointer:

withUnsafePointer(to: existential) { pointer in
    let bits = UInt(bitPattern: pointer)
    
    if let pointer = UnsafePointer<P.Type>(bitPattern: bits) {
        print(pointer)
        print(pointer.pointee)
    }
}

If it is long-lived, you need to do the allocation yourself:

let pointer = UnsafeMutablePointer<P.Type>.allocate(capacity: 1)
let bits = UInt(bitPattern: pointer)

pointer.initialize(to: existential)

if let pointer = UnsafePointer<P.Type>(bitPattern: bits) {
    print(pointer)
    print(pointer.pointee)
}

pointer.deinitialize(count: 1)
pointer.deallocate()

Don't forget to (de-init and) deallocate.

1 Like