[英]Possible to create statically allocated array in swift?
我想在swift中创建一个结构,它具有一个固定数量的小值(例如16个浮点数)作为实例数据。 要求此结构不将这些值存储在堆上,以便结构实例的地址是实例变量的地址。 还要求这些值可以通过下标在结构内部访问,就像数组一样。
在C中,你只需简单地定义这种东西:
struct Matrix4x4 {
float elements[16];
...
} myMatrix;
使用此代码, sizeof(Matrix4x4) == 64
以及&myMatrix == &myMatrix.elements[0];
在swift中,如果我类似地将elements
变量定义为类型[Float]
,则矩阵实例仅包含指向数组的指针,因为Array<Float>
实例是存储在堆上的对象。
有没有办法在swift中获得实例变量的静态分配而不放弃类似数组的下标访问的便利性和效率?
目前,这在“纯粹的斯威夫特”中是不可能的。 从一开始就对swift-evolution邮件列表进行了长时间的讨论
要求这样的特征,例如将矩阵结构传递给C函数。 据我所知,这个建议很受欢迎,但目前还没有具体的计划,也没有在目前活跃的Swift提案中列出。
交流阵列
float elements[16];
作为具有16个组件的元组导入Swift:
public var elements: (Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float)
目前,这似乎是定义具有给定内存布局的固定大小结构的唯一方法。 来自Apple的Joe Groff在[swift-users]将C语义映射到Swift
Swift结构具有未指定的布局。 如果依赖于特定的布局,则应该在C中定义结构并将其导入到Swift中。
后来在那次讨论中 :
您可以保留在C中定义的结构并将其导入Swift。 斯威夫特将尊重C的布局。
如果矩阵类型是在C头文件中定义的(为了简单起见,我现在使用2x2矩阵作为示例)
// matrix.h:
typedef struct Matrix2x2 {
float elements[4];
} Matrix2x2;
然后它被导入到Swift中
public struct Matrix2x2 {
public var elements: (Float, Float, Float, Float)
public init()
public init(elements: (Float, Float, Float, Float))
}
如上所述,Swift保留了C内存布局,因此矩阵,其元素和第一个元素都具有相同的地址:
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
但是,元组不是可订阅的,如果元组成员具有不同的类型,则这是有意义的。 关于swift-evolution邮件列表还有另一个讨论
将“统一元组”视为集合,这将允许下标。 不幸的是,这还没有实现。
有一些方法可以通过索引访问元组成员,例如使用Mirror()
或withUnsafe(Mutable)Pointer()
。
这是一个可能的Swift 3(Xcode 8)解决方案,它似乎运行良好,只需要很少的开销。 “技巧”是定义返回指向元素存储的指针的C函数:
// 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;
}
我们需要两个变体来使正确的值语义工作(下标setter需要一个变量,下标getter使用常量或变量)。 “swift_name”属性使编译器将这些函数作为Matrix2x2
类型的成员函数进行比较
现在我们可以在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
}
}
}
一切都按预期工作:
// 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))
当然,您也可以定义类似矩阵的下标方法
public subscript(row: Int, col: Int) -> Float
以类似的方式。
正如上面的答案所暗示的那样,你可以使用withUnsafeMutableBytes()
和withUnsafeMutableBytes()
assumingMemoryBound(to:)
来将C数组视为调用范围内的swift数组。
withUnsafeMutableBytes(of: &mymatrix.elements) { rawPtr in
let floatPtr = rawPtr.baseAddress!.assumingMemoryBound(to: Float.self)
// Use the floats (with no bounds checking)
// ...
for i in 0..<10 {
floatPtr[i] = 42.0
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.