前回の続きです。クロージャでクラスのインスタンス生成をすっきり書けそうなのでやってみました。
ここまでシンプルにかければ、バインドの為の外部ライブラリの力を借りずとも十分やっていけそうです。BindClassという新たな関数が追加され、こちらに雑多なコードが移動しています。BindClassがuserdataを生成するだけの関数(ラムダで定義)を引数としている点がポイントです。ラムダでは前回同様userdata上にplacement newでインスタンスを初期化します。
BindClassもこれだけで、特定のクラス名などが取り除かれ汎用化されています。CreateClassMetatableでメタテーブルを生成するのは定番のコードです。
C++にだけ慣れていると奇妙に見えるのはCreateCppClassInstanceです。これはLuaから呼べる関数型をしていますが単独では呼べません。upvalueという領域にクラス名とクラスのインスタンス生成を行う関数を結び付けてクロージャを生成しておくことで初めて関数として呼べるようになっています。そのクロージャを生成してクラス名と同名のグローバル関数を登録する部分がCreateClassInstanceCreatorです。
このようにしてクラスの種類数だけクロージャを生成することで、どんな種類のクラスインスタンスもCreateCppClassInstanceに生成させることが出来るようになります。
ところで、CreateCppClassInstanceのactualInstanceCreatorはlua_CFunction型として取り出して、直接CからactualInstanceCreatorを呼び出しています。これはBindClassの引数として受け取っているcreatorです。ということは、Luaが直接creatorを呼び出すことは無いのでlua_CFunction型を使う必要は無いということです。lightuserdataとして任意の型の関数にしても問題なさそうです。
Showing posts with label メタテーブル. Show all posts
Showing posts with label メタテーブル. Show all posts
Wednesday, February 18, 2015
Tuesday, February 10, 2015
Luaでシェーダ言語のベクトルのswizzlingのようなことをしてみる
3DをやっているとLuaからC++のベクトルや行列にアクセスしたいことがあります。今回の目標はLuaからC++のベクトルを作って、更にシェーダ言語のswizzlingみたいなことをしてみます。例えばこんな感じに書けるようになります。
これをやるためにはメタテーブルの"__index"と"__newindex"をそれぞれC関数として、実行時に解釈します。以下全ソースです。
swizzlingして読みだす時(右辺に来るとき)はVec4Index、swizzlingして書き込む時(左辺にくるとき)はVec4NewIndexが呼ばれます。
Vec4NewIndexで、代入する値がベクトルであってもスカラーであってもいいようにLUA_TNUMBERとLUA_TUSERDATAに分岐しています。
Luaからベクトルの長さを得るGetLengthというメソッドがあります。BindVec4関数内でバインドしようとしているのはだめな例です。なぜならBindVec4で作っているのはメタテーブルであって、__で始まるメタメソッドは定義出来ても任意のメソッドは定義できません。前回のMyClassはメタテーブルの__indexにメタテーブル自身を参照させていたのでメタテーブル内に任意のメソッドを定義することができましたが、今回は__indexをswizzlingのためのC関数であるVec4Indexにつかってしまっています。
ではどうするのかというと、Vec4Index関数の中で"GetLength"を参照しようとしたかを判定してVec4GetLength関数を返しています。これでLuaからGetLengthが使えます。
Vec4NewIndexで、代入する値がベクトルであってもスカラーであってもいいようにLUA_TNUMBERとLUA_TUSERDATAに分岐しています。
Luaからベクトルの長さを得るGetLengthというメソッドがあります。BindVec4関数内でバインドしようとしているのはだめな例です。なぜならBindVec4で作っているのはメタテーブルであって、__で始まるメタメソッドは定義出来ても任意のメソッドは定義できません。前回のMyClassはメタテーブルの__indexにメタテーブル自身を参照させていたのでメタテーブル内に任意のメソッドを定義することができましたが、今回は__indexをswizzlingのためのC関数であるVec4Indexにつかってしまっています。
ではどうするのかというと、Vec4Index関数の中で"GetLength"を参照しようとしたかを判定してVec4GetLength関数を返しています。これでLuaからGetLengthが使えます。
テストはこれでいいかもしれませんが、実際に使えるものにするにはもうひと工夫する必要がありそうです。
Saturday, February 7, 2015
Lua5.3を自力バインド
LuaとC++を自力でつないでみます。
以前はtolua++を使っていましたが、グルーコードを毎回生成するのが意外と不便でした。ヘッダの日付が変わってしまったために全体ビルドがかかってしまったりします。nmakeやバッチファイルを導入してみたり、自動生成したファイルをバージョン管理に含めるべきか悩んだりしました。結局外部定義ファイルよりはcppに直接バインド内容を書けてしまうほうが悩まないかもしれません。
例えば、WindowsのMessageBoxをLuaから使えるようにするコードです。あっけないほど簡単です。わざわざ外部のライブラリを導入する必要もないです。プロジェクトの性質によってはこれで十分用足りるでしょう。
ここからが本題です。C++を使うからにはC++っぽいことをしたいわけです。目指す仕様は、
GitHubはここです。
・C++のクラスをバインドしてLuaからインスタンスを作る
・グルーコードでnewからdeleteまで勝手にやってくれる
・ガーベージコレクションの時点でdeleteさせたい
・グルーコードでnewからdeleteまで勝手にやってくれる
・ガーベージコレクションの時点でdeleteさせたい
これを実現したコードが以下です。
C++のBindMyClass関数でLua内にMyClassというクラスをメタテーブルで定義します。MyClassはSetValueとGetValueという2つのメソッドを持ち、__gcでガーベージコレクションの直前に呼ぶ関数があることをLuaに教えます。__indexはやや特殊ですが、MyClassのインスタンスがメソッドを探しに行くテーブルがメタテーブル自身であることを示します。これによってメタテーブル内にメソッドを定義できます。
最後のlua_registerで"MyClass"という名前のLua関数を呼ぶとC++側のMyClassNew関数がインスタンスを生成して返します。クラス名と同じ名前の関数として登録していますが関数名は何でも構いません。MyClassNewではLuaのユーザーデータを生成してC++側で生成したMyClassのポインタを格納し、先ほど作っておいたMyClassのメタテーブルを設定します。
MyClassSetValueとMyClassGetValueはLua側からC++側の変数に値を格納したり取り出したりする所です。せっかくなのでLua5.3の新機能である整数形式を試してみます。lua_Integerはデフォルトで64bitになりました。よって、int型に代入する場合キャストしないとwarningが出ます。
一見複雑ですが、アセンブリを読むような難解さはあれど覚えることはそう多くはないです。一度自力でバインドする方法を試しておくと、たとえ他のオープンソースのバインダーを使う場合でもより理解しやすくなるはずです。
GitHubはここです。
Subscribe to:
Posts (Atom)