F# pointer member accessor
Asked Answered
S

1

6

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.

Saltworks answered 18/11, 2016 at 20:33 Comment(2)
To clarify - you have a NativePtr<some struct> and you want to set the named elements of this struct? Do you have an example of how you are currently using NativePtr.write to achieve this in F#?Hippie
Yes that's correct. I have made a function which takes the value I wish to update, the NativePtr and the dereferenced struct from that pointer: NativePtr.write ptr p. Prior to calling NativePtr.write, I have updated a member within the p struct.Saltworks
H
4

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.

Hippie answered 18/11, 2016 at 23:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.