小ネタです。一時的に配列の生ポインタが欲しかったので、RAIIでピン止めするクラスを作りました。
open System
open System.Runtime.InteropServices
type Pin(o) =
let gch = GCHandle.Alloc(o, GCHandleType.Pinned)
member x.Addr = gch.AddrOfPinnedObject()
interface IDisposable with member x.Dispose() = gch.Free()
一行do
で使う例を示します。
let a = [|0; 1; 2; 3|]
do use p = new Pin(a) in printfn "0x%x" p.Addr
P/Invoke
P/Invokeと組み合わせた例です。
[<DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)>]
extern int puts(nativeint s)
let s = [|'a'B; 'b'B; 'c'B; 0uy|]
do use p = new Pin(s) in puts p.Addr |> ignore
この例ではnativeint
で渡すために無理矢理ピン止めしていますが、普通はそんなことをする必要はありません。
[<DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)>]
extern int puts(byte[] s)
let s = [|'a'B; 'b'B; 'c'B; 0uy|]
puts s |> ignore
ピン止めのことを意識していれば、byte[]
を指定しても裏側ではマーシャリングでピン止めされている状況が想像できます。
引数にobj
を指定してもコンパイルは通りますが、実行時にアクセス違反が発生します。
アクセス違反
[<DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)>]
extern int puts(obj s)
let s = [|'a'B; 'b'B; 'c'B; 0uy|]
puts s |> ignore
動機
OpenGLには glVertexPointer() の第4引数pointer
のように、引数で渡す配列の型が可変な関数があります。型ごとに別のP/Invokeを定義する以外の方法として、一時的に生ポインタを取得することを試みました。
F#でのOpenGLについては以下の記事を参照してください。
- F#でOpenGL 2017.01.19