昨日一昨日くらいは、LuaからどのようにしてC++のクラスにアクセスするかを調査していた。 やりたいこととしては、ある環境(クラス)を定義して、それにLuaからアクセスすることでクラス内の挙動を変えたい。 具体的に言えば、Lua側からスクリプトを書くことでISSの挙動(最大命令実行数、ブレークポイントなど)を変更させたい。
これを実現するための方法を調べた。結論としては、Lua側から制御して新しいC++クラスをインスタンスし、Luaからアクセスするときは 必ずそれを参照する。
Luaからクラスを生成する。
以下のような関数を作成した。
#include <lua5.2/lua.hpp> #include <lua5.2/lualib.h> #include <lua5.2/lauxlib.h> #include <iostream> #include <string> #include "./lua_class.h" int MakeClass (lua_State *L) { // Getting Arguments std::string arch_name = lua_tostring (L, 1); int x = lua_tointeger (L, 2); std::cout << "ARCH_NAME=" << arch_name << '\n'; class lua_class *new_L = new lua_class (); new_L->set_x (x); lua_pushlightuserdata (L, static_cast<void *>(new_L)); return 1; } int GetX (lua_State *L) { // Getting Arguments class lua_class *target_L = static_cast<class lua_class *>(lua_touserdata (L, 1)); int x = target_L->get_x(); std::cout << "GetX() = " << x << '\n'; lua_pushinteger (L, x); return 1; } int SetX (lua_State *L) { // Getting Arguments class lua_class *target_L = static_cast<class lua_class *>(lua_touserdata (L, 1)); int x = lua_tointeger (L, 2); target_L->set_x(x); std::cout << "Set(" << x << ")\n"; return 0; }
ここで、lua_classは自前で作成したクラスで、変数としてm_xを持っており、set_x(), get_x()でアクセスできる。 例えば、Lua側から以下のようなプログラムを記載したとする。
core = make_core ("risc-v", 2) set_x (core, 1244) get_x (core)
まず、make_coreは2つ引数を取り、一つ目は文字列、2つ目は整数だ。 これによりMakeClassが呼ばれ、新しいlua_classクラスがnewされて、それが返り値として返される(lua_pushlightuserdata)。
make_coreがMakeClassにバインドされるのは、プログラム中で定義する。
lua_register(lua_state, "make_core", MakeClass); lua_register(lua_state, "get_x", GetX); lua_register(lua_state, "set_x", SetX);
クラスにアクセスして、変数を制御する
core = make_core ("risc-v", 2) set_x (core, 1244) get_x (core)
上記のプログラムでは,coreオブジェクトを第一引数、変数を第二引数としてset_xを呼び出す。 これにより、C++側のSetXが呼び出される。
まず、lua_touserdataにより、第一引数がlua_classのポインタに変換され、変数をセットする。 戻り値は、ここでは取らないため、return 0を返す。
次に、get_xも同様にlua_classのポインタを渡し、それをlua_class側にstatic_castで変換する。 そして、get_xメソッドを呼び出すことで、変数xの内容を返すという構造になっている。