简体   繁体   English

可以在swift中创建静态分配的数组吗?

[英]Possible to create statically allocated array in swift?

I want to create a struct in swift that has a small fixed number of values (say 16 floats) as instance data. 我想在swift中创建一个结构,它具有一个固定数量的小值(例如16个浮点数)作为实例数据。 It is required that this struct not store these values on the heap, so that the address of an instance of the struct is the address of the instance vars. 要求此结构不将这些值存储在堆上,以便结构实例的地址是实例变量的地址。 It is also a requirement that these values be accessible internally to the struct via subscript, as Arrays are. 还要求这些值可以通过下标在结构内部访问,就像数组一样。

In C you would simply define this kind of thing thusly: 在C中,你只需简单地定义这种东西:

struct Matrix4x4 {
    float elements[16];
    ...
} myMatrix;

With this code, sizeof(Matrix4x4) == 64 and also &myMatrix == &myMatrix.elements[0]; 使用此代码, sizeof(Matrix4x4) == 64以及&myMatrix == &myMatrix.elements[0]; In swift, if I analogously define the elements variable as type [Float] , the matrix instance only contains a pointer to the array, since the Array<Float> instance is an object stored on the heap. 在swift中,如果我类似地将elements变量定义为类型[Float] ,则矩阵实例仅包含指向数组的指针,因为Array<Float>实例是存储在堆上的对象。

Is there a way in swift to get static allocation of the instance vars without abandoning the convenience and efficiency of array-like subscripting access? 有没有办法在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邮件列表进行了长时间的讨论

which asks for such a feature, eg to pass a matrix structure to C functions. 要求这样的特征,例如将矩阵结构传递给C函数。 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 . 据我所知,这个建议很受欢迎,但目前还没有具体的计划,也没有在目前活跃的Swift提案中列出。

AC array 交流阵列

float elements[16];

is imported to Swift as a tuple with 16 components: 作为具有16个组件的元组导入Swift:

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 来自Apple的Joe Groff在[swift-users]将C语义映射到Swift

Swift structs have unspecified layout. Swift结构具有未指定的布局。 If you depend on a specific layout, you should define the struct in C and import it into Swift for now. 如果依赖于特定的布局,则应该在C中定义结构并将其导入到Swift中。

and later in that discussion : 后来在那次讨论中

You can leave the struct defined in C and import it into Swift. 您可以保留在C中定义的结构并将其导入Swift。 Swift will respect C's layout. 斯威夫特将尊重C的布局。

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) 如果矩阵类型是在C头文件中定义的(为了简单起见,我现在使用2x2矩阵作为示例)

// matrix.h:
typedef struct Matrix2x2 {
    float elements[4];
} Matrix2x2;

then it is imported to Swift as 然后它被导入到Swift中

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: 如上所述,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

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邮件列表还有另一个讨论

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, eg using Mirror() or withUnsafe(Mutable)Pointer() . 有一些方法可以通过索引访问元组成员,例如使用Mirror()withUnsafe(Mutable)Pointer()

Here is a possible solution for Swift 3 (Xcode 8) , which seems to work well and involves only little overhead. 这是一个可能的Swift 3(Xcode 8)解决方案,它似乎运行良好,只需要很少的开销。 The "trick" is to define C functions which return a pointer to the element storage: “技巧”是定义返回指向元素存储的指针的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;
}

We need two variants to make the proper value semantics work (subscript setter requires a variable, subscript getter works with constant or variable). 我们需要两个变体来使正确的值语义工作(下标setter需要一个变量,下标getter使用常量或变量)。 The "swift_name" attribute makes the compiler import these functions as member functions of the Matrix2x2 type, compare “swift_name”属性使编译器将这些函数作为Matrix2x2类型的成员函数进行比较

Now we can define the subscript methods in Swift: 现在我们可以在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. 以类似的方式。

As the answer above alludes, you can use combo of withUnsafeMutableBytes() and assumingMemoryBound(to:) to treat the C array as a swift array within the scope of the call. 正如上面的答案所暗示的那样,你可以使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM