Possible to create statically allocated array in swift?
At present, this is not possible in "pure Swift". There is a long discussion on the swift-evolution mailing list starting at
- [swift-evolution] Proposal: Contiguous Variables (A.K.A. Fixed Sized Array Type)
which asks for such a feature, e.g. to pass a matrix structure to C functions. As far as I can see, the suggestion was well-received, but nothing concrete is planned as of now, and it is not listed in the currently active Swift proposals.
A C array
float elements[16];
is imported to Swift as a tuple with 16 components:
public var elements: (Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float)
and at present this seems to be the only way to define a fixed-sized structure with a given memory layout. Joe Groff from Apple writes at [swift-users] Mapping C semantics to Swift
Swift structs have unspecified layout. If you depend on a specific layout, you should define the struct in C and import it into Swift for now.
and later in that discussion:
You can leave the struct defined in C and import it into Swift. Swift will respect C's layout.
If the matrix type is defined in a C header file (for the sake of simplicity I am using a 2x2 matrix as example now)
// matrix.h:
typedef struct Matrix2x2 {
float elements[4];
} Matrix2x2;
then it is imported to Swift as
public struct Matrix2x2 {
public var elements: (Float, Float, Float, Float)
public init()
public init(elements: (Float, Float, Float, Float))
}
As mentioned above, Swift preserves the C memory layout, so that the matrix, its elements, and the first element all have the same address:
var mat = Matrix2x2(elements: (1, 2, 3, 4))
print(sizeofValue(mat)) // 16
withUnsafePointer(&mat) { print($0) } // 0x00007fff5fbff808
withUnsafePointer(&mat.elements) { print($0) } // 0x00007fff5fbff808
withUnsafePointer(&mat.elements.0) { print($0) } // 0x00007fff5fbff808
However, tuples are not subscriptable, and that makes sense if the tuple members have different types. There is another discussion on the swift-evolution mailing list
- [swift-evolution] CollectionType on uniform tuples [forked off Contiguous Variables]
to treat "uniform tuples" as collections, which would allow subscripting. Unfortunately, this hasn't been implemented yet.
There are some methods to access tuple members by index, e.g. using Mirror()
or withUnsafe(Mutable)Pointer()
.
Here is a possible solution for Swift 3 (Xcode 8), which seems to work well and involves only little overhead. The "trick" is to define C functions which return a pointer to the element storage:
// matrix.h:
// Constant pointer to the matrix elements:
__attribute__((swift_name("Matrix2x2.pointerToElements(self:)")))
static inline const float * _Nonnull matrix2x2PointerToElements(const Matrix2x2 * _Nonnull mat)
{
return mat->elements;
}
// Mutable pointer to the matrix elements:
__attribute__((swift_name("Matrix2x2.pointerToMutableElements(self:)")))
static inline float * _Nonnull pointerToMutableElements(Matrix2x2 * _Nonnull mat)
{
return mat->elements;
}
We need two variants to make the proper value semantics work (subscript setter requires
a variable, subscript getter works with constant or variable).
The "swift_name" attribute makes the compiler import these functions as member
functions of the Matrix2x2
type, compare
- SE-0044 Import as member
Now we can define the subscript methods in Swift:
extension Matrix2x2 {
public subscript(idx: Int) -> Float {
get {
precondition(idx >= 0 && idx < 4)
return pointerToElements()[idx]
}
set(newValue) {
precondition(idx >= 0 && idx < 4)
pointerToMutableElements()[idx] = newValue
}
}
}
and everything works as expected:
// A constant matrix:
let mat = Matrix2x2(elements: (1, 2, 3, 4))
print(mat[0], mat[1], mat[2], mat[3]) // 1.0 2.0 3.0 4.0
// A variable copy:
var mat2 = mat
mat2[0] = 30.0
print(mat2) // Matrix2x2(elements: (30.0, 2.0, 3.0, 4.0))
Of course you could also define matrix-like subscript methods
public subscript(row: Int, col: Int) -> Float
in a similar manner.