可以在 swift 中创建静态分配的数组吗?
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有没有办法在不放弃类数组下标访问的便利性和效率的情况下获得实例变量的静态分配?
目前,这在 "pure Swift" 中是不可能的。有一个很长的讨论
在 swift-evolution 邮件列表中,起始于
要求这样的功能,例如将矩阵结构传递给 C 函数。
据我所知,这个建议很受欢迎,但没有具体计划
截至目前,它未在列表中列出
currently active Swift proposals.
一个C数组
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] 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.
You can leave the struct defined in C and import it into Swift. Swift will respect C's layout.
如果矩阵类型定义在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([=14=]) } // 0x00007fff5fbff808
withUnsafePointer(&mat.elements) { print([=14=]) } // 0x00007fff5fbff808
withUnsafePointer(&mat.elements.0) { print([=14=]) } // 0x00007fff5fbff808
但是,元组不可下标,如果元组成员有
不同种类。 swift-evolution 邮件列表
上还有另一个讨论
将 "uniform tuples" 视为集合,这将允许下标。
不幸的是,这还没有实现。
有一些方法可以通过索引访问元组成员,例如使用 Mirror()
或 withUnsafe(Mutable)Pointer()
。
这是 Swift 3 (Xcode 8) 的一个可能的解决方案,它似乎工作得很好
并且只涉及很少的开销。 "trick" 是定义 C 函数
return 指向元素存储的指针:
// 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()
和 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
}
}
我想在 swift 中创建一个结构,它具有少量固定值(比如 16 个浮点数)作为实例数据。要求此结构不将这些值存储在堆上,以便结构实例的地址是实例变量的地址。还要求结构可以通过下标在内部访问这些值,就像数组一样。
在 C 中,您可以简单地这样定义这种东西:
struct Matrix4x4 {
float elements[16];
...
} myMatrix;
使用此代码,sizeof(Matrix4x4) == 64
以及 &myMatrix == &myMatrix.elements[0];
在 swift 中,如果我类似地将 elements
变量定义为类型 [Float]
,则矩阵实例仅包含指向数组的指针,因为 Array<Float>
实例是存储在堆上的对象。
swift有没有办法在不放弃类数组下标访问的便利性和效率的情况下获得实例变量的静态分配?
目前,这在 "pure Swift" 中是不可能的。有一个很长的讨论 在 swift-evolution 邮件列表中,起始于
要求这样的功能,例如将矩阵结构传递给 C 函数。 据我所知,这个建议很受欢迎,但没有具体计划 截至目前,它未在列表中列出 currently active Swift proposals.
一个C数组
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] 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.
You can leave the struct defined in C and import it into Swift. Swift will respect C's layout.
如果矩阵类型定义在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([=14=]) } // 0x00007fff5fbff808
withUnsafePointer(&mat.elements) { print([=14=]) } // 0x00007fff5fbff808
withUnsafePointer(&mat.elements.0) { print([=14=]) } // 0x00007fff5fbff808
但是,元组不可下标,如果元组成员有 不同种类。 swift-evolution 邮件列表
上还有另一个讨论将 "uniform tuples" 视为集合,这将允许下标。 不幸的是,这还没有实现。
有一些方法可以通过索引访问元组成员,例如使用 Mirror()
或 withUnsafe(Mutable)Pointer()
。
这是 Swift 3 (Xcode 8) 的一个可能的解决方案,它似乎工作得很好 并且只涉及很少的开销。 "trick" 是定义 C 函数 return 指向元素存储的指针:
// 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()
和 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
}
}