Reputation: 1401
I'm new to F# and trying to communicate with a C-library from F#. I'm able to use several of the functions it provides but the Read() function is proving tricky since it has two parameters which are pointer and return the read content.
Here is the C function definition:
TPCANStatus __stdcall CAN_Read(
TPCANHandle Channel,
TPCANMsg* MessageBuffer,
TPCANTimestamp* TimestampBuffer);
And here is the C# definition:
[DllImport("PCANBasic.dll", EntryPoint = "CAN_Read")]
public static extern TPCANStatus Read(
[MarshalAs(UnmanagedType.U2)]
TPCANHandle Channel,
out TPCANMsg MessageBuffer,
out TPCANTimestamp TimestampBuffer);
And here is the F# version I'm working on:
module PCANBasic =
type TPCANHandle = uint16
[<Struct>]
type TPCANTimestamp =
val millis : uint32
val millisOverflow : uint16
val micros : uint16
new (_millis, _overflow, _micros) = {millis = _millis; millisOverflow = _overflow; micros = _micros}
[<Struct>]
type TPCANMsg =
val mutable id : uint32
[<MarshalAs(UnmanagedType.U1)>]
val mutable mstype : byte
val mutable len : byte
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)>]
val mutable data : byte array
new (_id, _mstype, _len, _data) = {id = _id; mstype = _mstype; len = _len; data = _data}
new (_id, _data) = {id = _id; mstype = 0x00uy; len = byte (Array.length _data); data = Array.concat [| _data; (Array.zeroCreate 8) |]}
module private Imported =
[<DllImport("PCANBasic.dll", EntryPoint="CAN_Read")>]
extern TPCANStatus Read(
[<MarshalAs(UnmanagedType.U2)>]
TPCANHandle Channel,
[<Out>]
TPCANMsg MessageBuffer,
[<Out>]
TPCANTimestamp TimestampBuffer)
let read (chan : TPCANHandle, [<Out>] msgbuf : TPCANMsg byref, [<Out>] tsbuf : TPCANTimestamp byref) =
Imported.Read(chan, msgbuf, tsbuf)
let main argv =
….
let mbuf = ref (PCANBasic.TPCANMsg(0x010u, Array.zeroCreate 8))
let tbuf = ref (PCANBasic.TPCANTimestamp(0u,0us,0us))
let status = PCANBasic.read(handle, mbuf, tbuf) //Error here
The last line shows an error:
Severity Code Description Project File Line Suppression State
Error FS0001 This expression was expected to have type
'byref<PCANBasic.TPCANMsg>'
but here has type
'PCANBasic.TPCANMsg ref' CANTestApp C:\Users\source\repos\CANTestApp\CANTestApp\Program.fs 189 Active
How can I correctly call this C function with pointers from F#? Do you need the [<Out>]
attribute when you use byref
?
Upvotes: 3
Views: 442
Reputation: 1286
Couple of options for this.
Create a byref
or outref
rather than a ref
by binding mbuf
and tbuf
as mutable values and then passing them "by reference" using &
.
let main argv =
...
let mutable mbuf = PCANBasic.TPCANMsg(0x010u, Array.zeroCreate 8)
let mutable tbuf = PCANBasic.TPCANTimestamp(0u,0us,0us)
let status = PCANBasic.read(handle, &mbuf, &tbuf)
Define read
as a member rather than a function to allow it to be invoked with ref
arguments rather than byref
. Your main
function can then stay the same other than referencing the static Reader.Read
method rather than the read
function.
type Reader =
static member Read (chan : TPCANHandle, [<Out>] msgbuf : TPCANMsg byref, [<Out>] tsbuf : TPCANTimestamp byref) =
Imported.Read(chan, msgbuf, tsbuf)
[<Out>]
AttributeLooking at F# RFC FS-1053 indicates that a byref<T>
argument with the [<Out>]
attribute is equivalent to an outref<T>
argument, so your read
function/member definition could be (slightly) shortened to:
let read (chan : TPCANHandle, msgbuf : TPCANMsg outref, tsbuf : TPCANTimestamp outref) =
Imported.Read(chan, msgbuf, tsbuf)
Upvotes: 3