Search code examples
c#.netf#interoppinvoke

F# pointer member accessor


Is there any way of accessing the members of a NativePtr struct and assigning a value to them, much in the same way that you can in C# in the below example?

(From MSDN)

CoOrds* p = &home; p -> x = 25;

Currently I'm making use of NativePtr.write, however I'm unsure whether this is the best/correct solution.

Thanks.


Solution

  • The way you described is the clearest way of doing this, presuming that you must deal with the structs in this manner. For completeness' sake, here's that method (with the implementation details of the packing omitted):

    open FSharp.NativeInterop
    
    [<StructLayout(...)>]
    type myStructure =
        struct
            val mutable a : int
            val mutable b : byte
        end
    
    let changeA pointer newA =
        let mutable structure = NativePtr.read pointer
        structure.a <- newA
        NativePtr.write pointer structure
    

    However, because you must know the exact offsets of each of the elements, you could also use this information to write directly to that field. F# does not provide a way of doing this using named identifiers, and its strict typing of the nativeptr<'T> type means that you cannot simply cast to the relevant pointer type. The NativePtr.set offset ptr function adds sizeof<'T> * offset, so this is also no use in this case.

    Let's say the type myStructure has the Packed attribute, for simplicity. The offsets are then 0 for a, and 4 for b. Throwing all caution to the wind, and completely abandoning the realm of managed memory, we could do:

    let changeB pointer newB =
        let bPtr =
            NativePtr.toNativeInt pointer
            |> (+) 4n
            |> NativePtr.ofNativeInt<byte>
        NativePtr.write bPtr newB
    

    or even:

    let changeMember pointer offset (value : 'T) =
        let pointer' =
            NativePtr.toNativeInt pointer
            |> (+) (nativeint offset)
            |> NativePtr.ofNativeInt<'T>
        NativePtr.write pointer' value
    

    I leave it an open question as to what the best method of dealing with these situations is, if they must be dealt with at all. I am inclined to go with the first, clearest method at the expense of additional memory use. The last, arbitrary offset method is to be avoided at all costs - if you must deal with adding raw offsets, it is much much better to wrap them in a more easily verifiable function like the second method, so the caller need not calculate the offset itself.