I'm trying to retrieve the offset of items in a struct with an key path (as described in SE-210).
Whenever I try to get the offset for an key path based on a generic type I get nil returned - what is the point I'm missing?
Goal is to use a generic function to provide a CustomPlaygroundDisplayConvertible representation of the memory layout for a bunch of "identical" structures created in Swift / bridged from C with different struct alignment options.
protocol StructureProtocol {
var u8 : UInt8 { get }
var u16 : UInt16 { get }
}
struct SampleStruct : StructureProtocol {
var u8 : UInt8 = 0
var u16 : UInt16 = 0
init() { }
}
func offsetWithProtocol<T : StructureProtocol> ( _ clazz: T ) -> Int? {
return MemoryLayout<T>.offset(of: \T.u16 )
}
func offsetWithStruct( _ : SampleStruct ) -> Int? {
return MemoryLayout<SampleStruct>.offset(of: \SampleStruct.u16 )
}
let s = SampleStruct()
let o1 = offsetWithProtocol( s ) // returns nil
let o2 = offsetWithStruct( s ) // returns an Int
The generic key path ends up referring to the declaration of the protocol requirement, StructureProtocol.u16, which is not a stored property. This difference between the protocol requirement declaration and the implementation would also be apparent if you tried to compare the \T.u16 and \SampleStruct.u16 key paths for equality, since they would appear to be unequal.
Hmm... Just was under the assumption it worked once I corrected the protocol var definitions to { get set }. But I get still different results even though all types in the code should be identical:
import Foundation
protocol StructureProtocol {
var u8 : UInt8 { get set }
var u16 : UInt16 { get set }
}
struct SampleStruct : StructureProtocol {
var u8 : UInt8 = 0
var u16 : UInt16 = 0
init() { }
}
let s = SampleStruct()
func offsetWithProtocol<T : StructureProtocol> ( _ clazz: T ) -> Int? {
let keyPath = \T.u16
print( "\(T.self) \(type(of: keyPath) ) \(MemoryLayout<T>.self)" )
// => SampleStruct WritableKeyPath<SampleStruct, UInt16> MemoryLayout<SampleStruct>
return MemoryLayout<T>.offset(of: keyPath )
}
func offsetWithStruct( _ : SampleStruct ) -> Int? {
let keypath = \SampleStruct.u16
print( "\(SampleStruct.self) \(type(of: keypath) ) \(MemoryLayout<SampleStruct>.self)" )
// => SampleStruct WritableKeyPath<SampleStruct, UInt16> MemoryLayout<SampleStruct>
return MemoryLayout<SampleStruct>.offset(of: keypath )
}
let o1 = offsetWithStruct( s ) // returns an Int
let o2 = offsetWithProtocol( s ) // returns nil