1

我正在尝试编写一种类型来表示 GPU 设备中的指针,它应该像一个数组一样,具有用于获取和设置的索引属性。如果元素类型是原始类型,我没有问题,但是当我使用结构时,我无法更改其成员的值。

请参阅此代码:

#nowarn "9"

open System
open System.Runtime.InteropServices

[<Struct;StructLayout(LayoutKind.Sequential)>]
type MyStruct =
    val mutable x : int
    val mutable y : int
    val mutable z : int

    override this.ToString() = sprintf "(%d,%d,%d)" this.x this.y this.z

let deviceOnly() = failwith "this function should be used in quotation only"

type DevicePtr<'T>(h:nativeint) =
    member this.Handle = h
    member this.Item with get (idx:int) : 'T = deviceOnly() and set (idx:int) (value:'T) : unit = deviceOnly()
    member this.Reinterpret<'T2>() = DevicePtr<'T2>(h)
    member this.Volatile() = DevicePtr<'T>(h)
    static member (+) (ptr:DevicePtr<'T>, offset:int) = DevicePtr<'T>(ptr.Handle + nativeint(offset * sizeof<'T>))

let test() =
    let mutable test1 = MyStruct()
    test1.x <- 1

let foo (collection:MyStruct[]) =
    collection.[0].x <- 1

let bar (collection:DevicePtr<MyStruct>) =
    collection.[0].x <- 1
    //error FS0257:
    // Invalid mutation of a constant expression.
    // Consider copying the expression to a mutable local, e.g. 'let mutable x = ...'.

因此,类型是 DevicePtr<'T>,并且它具有使用 get 和 set 方法索引的属性 Item。但是get方法只返回一个值'T,所以我不能改变它。但是系统阵列有效。

有人有这样的经验吗?创建一个像数组一样的类型?我希望索引属性的 get 函数返回一个可变的 ref 而不是一个值。

4

1 回答 1

2

您无法创建与数组等结构一起使用的类型,因为语言和运行时对其他类无法获得的数组进行了特殊处理。

对于数组,访问一个结构元素的表达式(如在您的foo函数中)会导致该元素被直接修改到位。这collection.[0]有效地设置了一个“指针”或“引用”到一个元素,.x然后将表达式应用于该元素,允许您在适当的位置操作对象。

对于其他类,索引器只是另一个函数,这意味着正在返回值的副本。因此,在bar函数中,collection.[0]创建了返回值的副本,而不是从数组中获得的引用。因为.x会修改临时副本,所以编译器会发出您看到的错误。(对于类似的代码,C# 或 VB 中会出现非常类似的警告。)正如消息所示,您需要创建一个变量来保存副本、修改副本并将其分配回collection.[0].

于 2013-04-04T04:32:24.700 回答