Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does F# have address-of & and pointer dereference * functions?

In C#, I write

unsafe void Main() {
  float f = 3.14f;
  int i = *(int*)&f;
}

Is it possible to translate this code to F#?

My understanding is that a pointer is represented by the nativeptr<'a> type, but I for the life of me I cannot find an equivalent of the address reference & and pointer dereference * operators. I suspect NativePtr.get may be the latter, but its implementation escapes me, as I do not understand IL well enough.

I know about BitConverter and Marshal, but am looking for a way to implement bit mangling without copying memory.

like image 825
kkm Avatar asked Dec 19 '22 18:12

kkm


1 Answers

The NativePtr.get and set functions read and write at an offset. If you need to read byte by byte, then use that. If you need to read at offset zero, you can use read and write instead, they will be slightly more performant.

The operator for taking "raw" address (as opposed to a byref<_> reference) is called && (see definition).

But there is some more trickery involved, such as: you need to mark the variable mutable before you can take its address, you can't just store a nativeptr<_> value, you need to convert it to nativeint, plus the nativeptr<_> values are strongly typed, so you need to convert between them via nativeint, etc.

The following snippet will do the equivalent of your C# code (step by step and with full type annotations for increased clarity):

open FSharp.NativeInterop

let Main() =
  let mutable x: float = 3.1415
  let floatPtr: nativeint = NativePtr.toNativeInt<float> &&x
  let intPtr: nativeptr<int> = floatPtr |> NativePtr.ofNativeInt<int>
  let asInt: int = NativeInterop.NativePtr.read intPtr
  asInt

Or a more compact version:

open FSharp.NativeInterop

let Main() =
  let mutable x = 3.1415
  &&x |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read<int>

Or package it for reuse:

// val inline readAs : x:'a -> 'b when 'a : unmanaged and 'b : unmanaged
let inline readAs (x: 'a) : 'b =
  let mutable x' = x
  &&x' |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read<'b>

let Main() =
  let i = readAs 3.1415 : int
  ()

Having said all the above, I completely agree with John Palmer and GuyCoder: please don't do this if at all possible. This looks like exactly the kind of premature optimization that Dr. Knuth warned us about.

like image 59
Fyodor Soikin Avatar answered Jan 13 '23 12:01

Fyodor Soikin