Showing posts with label Lua. Show all posts
Showing posts with label Lua. Show all posts

Saturday, December 5, 2015

Luaのrequireで独自ファイルシステム上のスクリプトをロード

requireでAndroidのAPKのassetsフォルダからスクリプトを読み込みたくて、方法を探ってみました。requireはCのincludeに似た使い方をしますが、requireの実体はモジュール名を引数に取るただの関数です。

dofileとrequireはよく似た関数ですが、いくつか違いがあります。まずrequireは通常拡張子".lua"無しでモジュールを指定します。これを実装してみます。


前回、dofileのファイルシステムを置き換えているので置き換え版のdofileを流用することでLuaだけで書けました。厳密にはpackage.pathを考慮していないという問題がありますが、これだけで用足りることも多いと思います。

ところで、requireで比較的重要なdofileとの違いが、重複requireガードです。これはpackage.loadedテーブルにロード結果が記録される事で行われます。実際にテーブルの中身をprintで出力してみると、以下のようになりました。(左がキー、右が値)
debug   table: 00C9D770
vec4    true
coroutine       table: 00C98AA8
_G      table: 00C92760
package table: 00C98828
math    table: 00C9A2D0
my_class        userdata: 00C9C3B8
io      table: 00C98C38
os      table: 00C99880
bind_win        true
string  table: 00C99FB0
utf8    table: 00C9D6D0
table   table: 00C98BE8
キーを見ると、luaL_openlibsでロードされるLuaの標準ライブラリに混じってrequireでロードしたモジュール(ここではvec4, bind_win, my_classがそれです)の名前が見えます。対して値はそのモジュールをロードした時のreturn値が格納されています。(return値が無い場合はtrue)

また、この値は同じモジュールの二回目以降のrequireで返される値としても使われます。以下の例は、dofileが毎回math.random()を実行するのに対して、requireは初回呼び出し時の値をpackage.loadedにキャッシュする事を確かめるものです。


以上を踏まえたファイルシステム置き換え版のrequireは以下のようになります。

Sunday, November 22, 2015

Luaのdofileで使うファイルシステムを置き換える

Luaはdofile等でスクリプトを読み込む時にfopenを使います。しかし、AndroidのAPKのassetsフォルダはfopenでアクセスできません。独自のファイルシステムを構築したい場合もあります。

そういう時は、以下のようにして、標準のdofileを上書きしてファイルシステムを置き換えます。



LoadFile関数はファイル名を受け取ってmallocで確保したメモリブロックにファイルの内容を格納して返すようにしました。例えばAndroidの場合はassetsフォルダからファイルを持ってくるようにします。このエントリを参考にしてください。

luaL_loadbuffer関数にスクリプトの内容とファイル名を渡します。ファイル名は万一スクリプト内でエラーが発生した場合にLuaがどのファイルの何行目なのかを知らせてくれる為に重要です。

lua_pcallは、読み込んだスクリプトを一度「実行」します。Cのような言語と違い、Luaは関数や変数の定義も「実行」することで行われますので、これをしないとスクリプトを読み込まなかったのと同じことになります。

luaL_loadbuffer又はlua_pcallでエラーが発生した場合、Luaスタックの最上位にエラーメッセージが入ります。ここでは、適当にメッセージを加工してluaL_errorに渡します。これによって、dofileの呼び出し元のスクリプト側のエラーとして処理されます。

最後のreturn lua_gettop(L) - 1;lua_pcallがスタック何かを積んだ場合、その個数を呼び出し元に返します。-1するのはdofileが受け取ったファイル名がスタックを一つ使っているのでそれを除外するためです。説明より実例のほうがわかりやすいと思うので、以下に書いてみました。例えば、以下のような事ができるようになります。



呼びだされたcallee.luaの最後の行にreturn文があり、その戻り値をcaller.luaで受け取っています。こういった使い方が必要ない場合は、return lua_gettop(L) - 1;はreturn 0;に置き換えても不都合はありません。ただし、caller.luaはdofileから戻り値を受け取れなくなります。

Wednesday, February 18, 2015

DIY: Binding Lua5.3 with C++11

Let's bind C++ stuff to Lua5.3! :)

THE MEAT AND POTATOES


It might be enough to simple scripting for several projects.


BRUTEFORCE WAY


Our main weapon is C++, so the above code doesn't satisfies me. So, let's bind C++ classes and create instances from Lua.

It works fantastic. Then MyClass is able to be generated with scripting, and the garbage collector calls our destructor and delete it.
However, it might be unpleasant chore to make additional method and classes. The bind code is too long and messy!

HACKING


Let's make stuff fun. The most brilliant thing for us is we are living in future. Now, C++11 allow us what we couldn't do before.

Below code is all to bind MyClass:


Most simple binds are able to be written with lambda. So, for compile it C++11 must be enabled. To prevent crashes by scripting bug, define a GET_MYCLASS macro to ensure the first element of the Lua stack is our MyClass. Note that MyClass instances are constructed with placement new. In this case, the destructor must be called directly instead of calling delete operator.

BindClass defined like below:


As you can see, there is no MyClass dependencies. To avoid dependencies, I made a closer generator and put the class names and userdata creators into upvalue. Of course, BindClass can also be used for binding two or more extra classes.

CONCLUSION


Binding to Lua is not so complex and easy to learn. It's only combinations of small number of features such as stack and metatables. I saw programmers bothered increasing binding codes. I bet we can simplify them. Hope this helps to find your own solution.

自力Luaバインド 決定版

前回の続きです。クロージャでクラスのインスタンス生成をすっきり書けそうなのでやってみました。 ここまでシンプルにかければ、バインドの為の外部ライブラリの力を借りずとも十分やっていけそうです。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として任意の型の関数にしても問題なさそうです。

LuaのC++バインドをラムダ式で書く

以前LuaからC++のクラスを使えるように自前バインドを書きました。しかし、非常に冗長でした。規模の大きいクラスになると恐ろしい長さになりそうです。そこで、C++11の恩恵に与りながら短く書き直してみました。

まず、luaL_setfuncsで複数関数を一気に登録します。その際luaL_Regの配列を渡すのですが、関数部をラムダ式で書きます。MyClassのポインタはGET_MYCLASSというマクロで取得し、更に万一型が合わないなどで取得に失敗してもクラッシュしないようになっています。

また、placement newでMyClassのメモリ確保をluaに任せるようにします。これによってダブルポインタを使う無駄を省いた上にコードもすっきりしました。deleteのかわりにp->~MyClassを明示的に呼ぶ必要があります。placement newを使うためには #include <new>が必要です。

単独のサンプルとしてはこのくらいが最短かと思いますが、複数のクラスをバインドするなら上のコードの一部を共通化できるのでもう少し短くなりそうです。例えばメタテーブルを生成して__indexに自身を指定するあたりはサブルーチン化します。

MyClassNewはマクロ化すれば複数クラスで使いまわせそうです。本当はテンプレート関数化したいところですが、メタテーブルに渡す文字列をテンプレート引数にするにはややトリックが必要です。現状のC++では文字列定数をテンプレート引数に出来ないためです。


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が使えます。

テストはこれでいいかもしれませんが、実際に使えるものにするにはもうひと工夫する必要がありそうです。


Monday, February 9, 2015

Lua stack dump (WRONG CASE)

This is very simple Lua stack dumper. It extracts all element values by using lua_tostring whatever the types is not a string. It looks work well, but the problem is that lua_tostring overwrites values in the stack by string version.

One of correct code is below. The programmer always needed to take a step to ensure their correct types when accessing the Lua stack.

Saturday, February 7, 2015

Lua5.3を自力バインド

LuaとC++を自力でつないでみます。

以前はtolua++を使っていましたが、グルーコードを毎回生成するのが意外と不便でした。ヘッダの日付が変わってしまったために全体ビルドがかかってしまったりします。nmakeやバッチファイルを導入してみたり、自動生成したファイルをバージョン管理に含めるべきか悩んだりしました。結局外部定義ファイルよりはcppに直接バインド内容を書けてしまうほうが悩まないかもしれません。

例えば、WindowsのMessageBoxをLuaから使えるようにするコードです。あっけないほど簡単です。わざわざ外部のライブラリを導入する必要もないです。プロジェクトの性質によってはこれで十分用足りるでしょう。

ここからが本題です。C++を使うからにはC++っぽいことをしたいわけです。目指す仕様は、
・C++のクラスをバインドしてLuaからインスタンスを作る
・グルーコードで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はここです。