Welp, it looks like ManagedBuffer isn’t going to work out for my needs, since I can’t figure out how to alter the parameter pack I pass to the Elements type parameter. And since ManagedRawBuffer doesn’t exist yet, that means I am left rolling my own storage based on UnsafeRawBufferPointer.
Since the layout can be determined dynamically, and I want to offer field-level access, I will need to store the offset of each field. @Slava_Pestov’s comment implied this might require writing a type-erased wrapper around Field.Type, since metatypes conforming to Field are not subtypes of the Field.Type existential. But the introduction of explicit any has given us the ability to spell a different type which seems to fit the bill:
protocol Field: BitwiseCopyable { }
struct FirstName: Field { }
struct LastName: Field { }
struct Table {
let storage: UnsafeRawBufferPointer
let fields: [any (Field.Type)]
let offsets: [Int]
struct Table {
// TODO: move this to a class so it can be refcounted (and ideally tail-allocated)
var storage: UnsafeMutableRawBufferPointer
var fields: [any (Field.Type)] = .init()
var offsets: [Int]
func _stride<F: Field>(of field: F.Type) -> (stride: Int) { MemoryLayout<F>.stride }
init<each F: Field>(fields fieldTypes: repeat (each F).Type) {
var lastFieldEnd = 0
for fieldType in repeat each fieldTypes {
fields.append(fieldType)
let offset = lastFieldEnd // TODO: round up to respect alignment of this field type
offsets.append(lastFieldEnd)
lastFieldEnd += _stride(of: fieldType)
}
// TODO: compute offset of and initialize storage buffer
}
}
One surprise was the need to explicitly specify the type of fields. If I try to use type inference from the initialization expression, the compiler interprets any as referring to the SIMD.any function:
var fields = [any (Field.Type)]()
// ^ error: cannot convert value of type '(any Field.Type).Type' to expected argument type 'SIMDMask<Storage>'
// ^ error: generic parameter 'Storage' could not be inferred
// ^ error: cannot call value of non-function type '[Bool]'
Edit: one more weird thing… the _stride(of:) function doesn’t work. But I can fall back to the old approach of declaring static properties an extension on Field and calling them on an instance of any (Field.Type):
extension Field {
var stride: Int { MemoryLayout<Self>.stride }
}
/* ... */
init<each F: Field>(fields fieldTypes: repeat (each F).Type) {
for fieldType in repeat each fieldTypes {
lastFieldEnd += fieldType.stride // this works!
}
I guess this is a bug in implicitly opened existentials?