I see what you mean. Your solution might be simpler for your specific use case.
But that Float_x51
was just meant as an example of a type that can implement the FixedSizeArray
protocol. In your scenario that would have been the Uniforms struct imported from C.
That is, in your project you have this:
// my-c-types.h
typedef struct {
float weights[51];
} Uniforms;
And you have a bridging header file with #import "my-c-types.h"
.
Then, you'd use the FixedSizeArray
protocol as follows (no matter if the C array has 51, 42 or 123 elements):
extension Uniforms : FixedSizeArray { // This is all you have to do to
static var count: Int { 51 } // add subscript access to a
typealias Element = Float // C imported type like Uniforms.
}
func test() {
var u = Uniforms()
for i in 0 ..< u.count {
u[i] = Float(i) // Or whatever you'd like to set the values to.
}
}
test()
And, as long as you use the regular subscript (rather than the unchecked one) it will trap if you accidentally specified a count
or Element
that doesn't match the memory layout of the C struct.
Since the protocol has subscripts and count, it can easily be extended to implement RandomAccessCollection
.
Like this.
protocol FixedSizeArray : RandomAccessCollection {
associatedtype Element
static var count: Int { get }
}
extension FixedSizeArray {
public var count: Int { return Self.count }
public var startIndex: Int { 0 }
public var endIndex: Int { Self.count }
public subscript(unchecked index: Int) -> Element {
get {
return withUnsafeBytes(of: self) { (ptr) -> Element in
let o = index &* MemoryLayout<Element>.stride
return ptr.load(fromByteOffset: o, as: Element.self)
}
}
set {
withUnsafeMutableBytes(of: &self) { (ptr) -> Void in
let o = index &* MemoryLayout<Element>.stride
ptr.storeBytes(of: newValue, toByteOffset: o, as: Element.self)
}
}
}
public subscript(index: Int) -> Element {
get {
typealias ML<T> = MemoryLayout<T>
precondition(ML<Self>.alignment == ML<Element>.alignment)
precondition(Self.count * ML<Element>.stride == ML<Self>.stride)
precondition(0 <= index && index < Self.count)
return self[unchecked: index]
}
set {
typealias ML<T> = MemoryLayout<T>
precondition(ML<Self>.alignment == ML<Element>.alignment)
precondition(Self.count * ML<Element>.stride == ML<Self>.stride)
precondition(0 <= index && index < Self.count)
self[unchecked: index] = newValue
}
}
}
If the Uniforms
C struct had contained not only the array, you'd have to modify the protocol so that you conform by also specifying a byteOffset
to where the array starts (as well as count
and Element
). But in such cases you'd probably want to use an all together different approach.