Luaの関数をC側で記録し、いつでも呼び出せるようにする
概要
例えば、関数myfuncをLuaで定義し、それを登録する関数registerMyFunctionおよび実行する関数doMyFunctionをC側で用意するとする。すなわち、テストコードは次の通り :
local myfunc = function() print("test_code"); end registerMyFunction(myfunc); doMyFunction(); doMyFunction();
想定される出力は当然、
test_code
test_code
である。
ポイント
ポイントは、与えられたデータをリファレンスに置き換えることで、その値をC側で保持し、利用することである。luaL_ref関数は、スタックトップにあるテーブル(値)のリファレンスをあるテーブルに記録し返す関数である。
そのあるテーブルとして、もちろんプログラム中に自分でグローバルなテーブルを作ってもよいが、あらかじめ用意されているレジストリ、それを表す疑似インデックスLUA_REGISTRYINDEXを用いることでそのような煩雑な作業を避けられる。
// スタックに関数を積む int ref = luaL_ref(L, LUA_REGISTRYINDEX); // refに対応する関数をスタックに積む lua_rawgeti(L, LUA_REGISTRYINDEX, ref); // 関数を呼び出す if(lua_pcall(L, 0, 0, 0) != LUA_OK) { throw std::exception(lua_tostring(L, -1)); }
実行例
#include <iostream> #include <Lua53/lua.hpp> // 自分で勝手に登録したヘッダです int g_ref; int L_registerMyFunction(lua_State* L) { g_ref = luaL_ref(L, LUA_REGISTRYINDEX); return 0; } int L_doMyFunction(lua_State* L) { lua_rawgeti(L, LUA_REGISTRYINDEX, g_ref); if(lua_pcall(L, 0, 0, 0) != LUA_OK) { throw std::exception(lua_tostring(L, -1)); } return 0; } int main() { auto L = luaL_newstate(); luaL_openlibs(L); lua_pushcfunction(L, L_registerMyFunction); lua_setglobal(L, "registerMyFunction"); lua_pushcfunction(L, L_doMyFunction); lua_setglobal(L, "doMyFunction"); try { if(luaL_loadfile(L, "lua.lua") || lua_pcall(L, 0, 0, 0)) { throw std::exception(lua_tostring(L, 1)); } } catch(std::exception& e) { std::cout << e.what() << std::endl; } std::cin.get(); return 0; }
グローバル変数がナウくない?
前回記事の通り、上位値を登録できるようにしましょう。ただ、オブジェクトを作るごとに関数を登録するのがいけてない…
#include <iostream> #include <Lua53/lua.hpp> class MyClass { public: static int L_constructor(lua_State* L) { const luaL_Reg funcs[] = { { "register", L_register }, { "execute", L_execute }, { NULL, NULL } }; luaL_newlibtable(L, funcs); auto self = static_cast<MyClass*>(lua_newuserdata(L, sizeof(MyClass))); new(self)MyClass(L); luaL_setfuncs(L, funcs, 1); return 1; } MyClass(lua_State* L) : L(L), ref(0) { } static int L_register(lua_State* L) { auto self = static_cast<MyClass*>(lua_touserdata(L, lua_upvalueindex(1))); self->ref = luaL_ref(L, LUA_REGISTRYINDEX); return 0; } static int L_execute(lua_State* L) { auto self = static_cast<MyClass*>(lua_touserdata(L, lua_upvalueindex(1))); lua_rawgeti(L, LUA_REGISTRYINDEX, self->ref); if(lua_pcall(L, 0, 0, 0) != LUA_OK) { throw std::exception(lua_tostring(L, -1)); } return 0; } int ref; lua_State* L; }; int main() { auto L = luaL_newstate(); luaL_openlibs(L); lua_pushcfunction(L, MyClass::L_constructor); lua_setglobal(L, "createMyClass"); try { if(luaL_loadfile(L, "lua.lua") || lua_pcall(L, 0, 0, 0)) { throw std::exception(lua_tostring(L, 1)); } } catch(std::exception& e) { std::cout << e.what() << std::endl; } std::cin.get(); return 0; }