クロージャーを纏めて登録
C++の関数をLuaから呼び出す
C++をLuaから呼び出す際、C++側のクラスのなかのメソッドを呼び出したい場合、そのクラスのポインタが必要となる。グローバル変数に代入するのは気持ちが悪いので、Luaのクロージャーにポインタを記憶させ、関数が呼ばれるたびにそこから引っ張りだすことを考える。
結論から言うと次の通り:
#include <iostream> #include <Lua53beta\lua.hpp> #include <exception> using namespace std; class Lua { public: Lua() { // lua_Stateの生成 L = luaL_newstate(); // Lua標準ライブラリの読み込み luaL_openlibs(L); } ~Lua() { lua_close(L); } protected: void registerClosure(lua_CFunction function, const char* name, void* value) const { // 値をプッシュし lua_pushlightuserdata(L, value); // その値を元にするクロージャを生成し lua_pushcclosure(L, function, 1); // そのクロージャをグロバール変数nameとする lua_setglobal(L, name); } void loadFile(const char* file_name) const { if(luaL_loadfile(L, file_name) || lua_pcall(L, 0, 0, 0)) { throw exception(lua_tostring(L, 1)); } } lua_State* L; }; class MyLua : public Lua { public: MyLua() : Lua() { const luaL_Reg funcs[] = { { "func", MyLua::L_Function }, { "func2", MyLua::L_Function2 }, { NULL, NULL } }; luaL_newlibtable(L, funcs); lua_pushlightuserdata(L, this); luaL_setfuncs(L, funcs, 1); lua_setglobal(L, "test"); loadFile("sample.lua"); } static int L_Function(lua_State* L) { MyLua* self = (MyLua*)lua_touserdata(L, lua_upvalueindex(1)); int number = lua_tointeger(L, -1); self->number = number; cout << "Function " << number << endl; return 0; } static int L_Function2(lua_State* L) { MyLua* self = (MyLua*)lua_touserdata(L, lua_upvalueindex(1)); cout << "Function2 " << self->number << endl; return 0; } private: int number; }; using namespace std; int main() { try { MyLua mylua; } catch(const std::exception& e) { cout << e.what() << endl; } cin.get(); }
自分用のライブラリ空間testを作り、test.func(10); test.func2();などとして呼び出す。
luaL_newlibtable(L, funcs); lua_pushlightuserdata(L, this); luaL_setfuncs(L, funcs, 1); lua_setglobal(L, "test");
の部分がライブラリを作る部分である。関数群funcを登録するのだが、luaL_newlibtableで空間を予約し、次にスタックにこのクラスのポインタthisをLightUserDataとしてプッシュする。そしてsetfuncsを呼び出し、そのスタックのトップにある値1個分を記録させ、その前のライブラリのテーブルに関数群を登録し、関数群funcsが呼び出されるたびに上記の値をアップバリュー(上位値)として取得できるようにする。
最後にそのライブラリテーブルにグローバル名testを付けて、いつでも呼び出されるようにするという寸法だ。
いざ関数が呼び出されると、
MyLua* self = (MyLua*)lua_touserdata(L, lua_upvalueindex(1));
によってポインタを取得できる。lua_upvalueindexは上位値のインデックスに変換する関数で、今回は1つしかないので1を指定してある。2つ以上ある場合はここの数字を変えて取り出す。
なお引数は lua_tointeger(L, -1) のように普通に取り出していく。もちろん-1でなくて1でも動く。