CのDLLをF#から呼びたい俺が辿った軌跡

はじめに

Cで作ったDLLをF#から呼び出す方法を探していたら、どうもplatform invokeだのmarshallingだのを気にしないとだめらしい。C#を使わない俺はその辺よくわかってないんだが、F#でやっている例として

がよくまとめっていて良さそうだなと思ってマネしようと思った。んで、そこで使っているF# PowerPackが、

を見ると、F# PowerPack自体はより細かいパッケージへと分裂していったようだ。あ、あれ?どう真似したらいいの、ぼく?
そして、俺の旅がはじまった。

注意

C++だといろいろめんどいので、全部CのコードとしてDLLからexportすることにした。

試行錯誤をまとめたコードとその結果

sandbox/CallCppFromFs at master · teramonagi/sandbox · GitHub
にある。俺用。

はじめは

のあたりを見ていたのだが、カーベジコレクションされないようにメモリ確保するのどうやんのかよくわかんなくて、適当にコード書いたら動いたのがあったのでそれでいきたい。Cでやむなく書く部分ってのは、大体落っこちてる巣値計算ライブラリを叩くためだったりするわけなので、

  • 整/小数
  • それらの配列
  • 文字

というネイティブな型が受け渡しできればよいだろう。その辺をまとめておくと以下の表。

C++ F#
int int
double* float[]/nativeint
char char
char* string(入力のみ?)
struct []属性を付けてtypeする
関数ポインタ delegate(stdcallのみっぽい)

C++の中で確保したメモリは

  • nativeint型(System.IntPtrの型略称)

になっていて、こいつをちゃんとその他の型のポインタにキャストしたい場合は

  • NativePtr.ofNativeInt

を用いる。そして、その要素を取得したい場合は

  • NativePtr.get

を用いる。

また

でnativeptrにキャストできる模様。

関数ポインタはC側で

  • typedef double(__stdcall *Function)(double, double);

のようにtypedefしたものをF#側で

  • type Func = delegate of float * float -> float

のようにdelegateを使って宣言する。括弧つき

  • type Func = delegate of (float * float) -> float

にするとダメだった、意味が変わってくるのかな?
この時の呼び出し規約はどうもstdcallのみっぽい。
これは、F#3.1の言語仕様のドラフトっぽいもの

をみると、呼び出し規約をいじれる属性であるUnmanagedFunctionPointerAttributeを使うなと書いてある。なんで、関数ポインタとしてはstdcallしか使えないっぽい。

参考

とりあえずこいつの18章に比較的詳しく記載があるのは見た。構造体やクラスの受け渡しもちゃんと載ってる。

monoのページだけど、マーシャリング周りの理解に良い。

ちゃんとやるならこれなのか?サンプルコードがググってもかからなくて、使い方がわからん。。。

データ「C++⇔F#」間相互変換周りはこの辺を読む必要がある。