Hi, I'm currently working on a project which heavily interoperates with C. To ease the work with all the pointers, I have a very useful extension for UnsafePointer (and UnsafeMutablePointer) that looks like this (copied from here):
extension UnsafePointer {
subscript<T>(_ keyPath: KeyPath<Pointee, T>) -> UnsafePointer<T> {
let raw = UnsafeRawPointer(self)
// If a key path is not directly-addressable I consider it programmer error
let offset = MemoryLayout<Pointee>.offset(of: keyPath)!
return raw.advanced(by: offset).assumingMemoryBound(to: T.self)
}
}
extension UnsafeMutablePointer {
subscript<T>(_ keyPath: KeyPath<Pointee, T>) -> UnsafeMutablePointer<T> {
let raw = UnsafeMutableRawPointer(self)
// If a key path is not directly-addressable I consider it programmer error
let offset = MemoryLayout<Pointee>.offset(of: keyPath)!
return raw.advanced(by: offset).assumingMemoryBound(to: T.self)
}
}
This allows me to get a pointer to a specific member of the value stored in the original pointer.
Most of the times it works exactly as it should, but when the KeyPath refers to a member, which is an UnsafePointer as well, then strangely enough the call to MemoryLayout.offset(of:) returns nil which crashes the program. Even stranger is that it doesn't crash, when I don't use the subscript.
If I have e.g. this c struct:
struct Foo {
int bar;
int length;
char * buffer;
};
The program crashes when I do this:
let fooPointer: UnsafeMutablePointer<Foo> = ...
let pointerToBufferPointer = fooPointer[\Foo.buffer]
// should be UnsafeMutablePointer<UnsafeMutablePointer<Int8>>, but crashes because offset is nil in subscript
But it works fine when I do the exact same work as the subscript does myself:
let fooPointer: UnsafeMutablePointer<Foo> = ...
let raw = UnsafeMutableRawPointer(fooPointer)
let bufferPointerOffset = MemoryLayout<Foo>.offset(of: [\Foo.buffer])!
let pointerToBufferPointer = raw.advanced(by: bufferPointerOffset).assumingMemoryBound(to: UnsafeMutablePointer<Int8>.self)
Why doesn't the subscript work, but my code does, even though they are doing the same thing? And why is that only the case, when the value I want is itself a pointer (maybe there are other cases, I didn't test everything yet)?
EDIT:
I tested this a bit more and I didn't get this error with a Swift struct in pure Swift code. Maybe this only occurs with C structs? Or only when the function where I want to do this is passed as a C function pointer to a C function?