lua による imgui 計画の準備として、 libclang によるバインディング生成器を luajit に移植してみた。
https://github.com/ousttrue/luajitffi
libclang に対してはだいだい動くようになって、自身で生成した FFI で動作するところまでできた。 また、 EmmyLua Annotation もある程度付与できた。
https://github.com/ousttrue/luajitffi/blob/master/clang/mod.lua
こいつで、 imgui.h から luajit FFI を生成する。
libclang
c(c++)ヘッダーを clang_visitChildren により、カーソルのTree としてパースする。 今回は、 CXChildVisit_Recurse で全部のカーソルをパースすることにした。
同じカーソルが複数個所に現れうる
循環しうる(Link List ?)
がありうることを考慮しておく。 clang-c/Index.h は 6000 カーソルくらいなので問題ないが、 Windows.h とかは 150000 カーソルとか爆発する。
わりと色んなところで型がネストしていてつらい
struct の中は namespace なので何でもあり
anonymous な union や struct のその場定義
typedef struct などのその場定義
関数ポインタのその場定義
要するに、Cのコードの書き方によってどのようなカーソル構造になるかのパターンを知っている必要があって、 パターン毎に分岐して情報を収集する必要がある。 ある型のメンバーの情報を集めていると、ネストした別の型情報が現れる場合があるので切り分ける。
基本的なパターン
FFI では、対象となる関数を起点にその関数が使用するすべての型の定義を取り込む。 カーソルはCのTranslationUnitの木構造をあらわしていて、型をあらわしていない。 カーソルから頑張って型を得る。 型を得られるカーソルは決まっていて、CXCursorType が宣言Declの系列となる。
TODO
カーソル FunctionDecl
カーソル EnumDecl
カーソル TypedefDecl
カーソル StructDecl
Type Pointer
Type Array
Type Elaborated
Type Record
Type FunctionProto 関数ポインタ pointer => functionproto
union
typedef struct
c++ name mangling
マクロとの戦い
luajit ffi
ffi.cdef に素直に定義すればいいので、他の言語の FFI に比べて簡単。
はまり。
ffi.load の返り値が GC されると関数ポインタが死ぬ
pointer は ffi.new('TYPE[1]') のようにサイズ1の array で運用する
tostring と ffi.string は違う
nullptr は nil ?
だいたいよきに計らってくれるので、 rust の FFI に比べて簡単なのであった。
ひとつだけはまりがあって、 struct の値渡しができない場合がある。
http://wiki.luajit.org/FFI-Callbacks-with-pass-by-value-structs
そういえば、 rust でも struct の値渡しではまった記憶が。
https://forum.dlang.org/thread/dkamxcamwttszxwwxttv@forum.dlang.org
rust の場合は、 struct の値返しが動かなかった。 これ、C の方で pointer 経由で値を返すラッパーを定義する必要があって回避方法はなかった。
ImVec2 ImGui::GetContentRegionAvail()
luajit ffi でもできるか注意が必要だな。
lfs への依存を FFI した Windows API で置き換える
現状、ファイル操作 isExists, mkDir のために lfs を使っているのだけど、 FFI で Windows API にアクセスできるようにしたら lfs 無しにできそう。 となれば luarocks も無しにできるので、 必要なのは luajit.exe だけになる。