To round this out, I wrote this as a global function, so that structs with tuple pseudo-arrays can be accessed without having to extend each one explicitly. My version:
func withUnsafeMutableBufferPointer<Struct,Element,Tuple,R>(
to value: inout Struct,
at property: WritableKeyPath<Struct,Tuple>,
using type: Element.Type,
_ body: (UnsafeMutableBufferPointer<Element>) throws -> R) rethrows -> R {
return try withUnsafeMutablePointer(to: &value [keyPath: property]) {
let count = MemoryLayout<Tuple>.stride / MemoryLayout<Element>.stride
return try $0.withMemoryRebound(to: Element.self, capacity: count) {
return try body(UnsafeMutableBufferPointer(start: $0, count: count))
}
}
}
used like this:
struct Uniforms { // assume this came from an imported header
var weights: (Float, Float, Float, Float)
}
var uniforms = Uniforms(weights: (1,2,3,4))
print("Uniforms: \(uniforms)") // 'Uniforms: Uniforms(weights: (1.0, 2.0, 3.0, 4.0))'
withUnsafeMutableBufferPointer(to: &uniforms, at: \.weights, using: Float.self) {
for index in 0 ..< $0.count {
$0 [index] *= 2
}
}
print("Uniforms: \(uniforms)") // 'Uniforms: Uniforms(weights: (2.0, 4.0, 6.0, 8.0))'
Ideally, though, I'd like to see the C header importer conform such structs to a protocol that provides this functionality as an instance method. I'd also like to see a distinction between accessing a fixed size C array (Unsafe[Mutable]BufferPointer
) and a variable size C array (Unsafe[Mutable]Pointer
).