主に必要な手続きは以下。
- void *lua_newuserdata (lua_State *L, size_t size)でユーザーデータ用のメモリブロックを確保
- 確保したメモリブロック内に、対象クラスのインスタンスを配置
- Luaスクリプト側に公開するメソッドを__indexメタメソッドへ登録
各ユーザーデータで取り違え(?)が生じないように以下を実施。
識別用のメタテーブルをC++側で設定しておき、Luaスクリプト側へ渡す。
(口述するサンプルコードではメタテーブルに紐づく名前を"person_type"としている)
Luaスクリプト側からユーザーデータを受け取った際(luaL_checkudata呼び出し時)には正しい型か否かを上記メタテーブルを介して検証する。
local obj1 = person.new(20,"Luna") print( obj1:Info() ) obj1:ChangeName("Luuga") print( obj1:Info() ) local obj2 = person.new() print( obj2:Info() )
期待結果
C++側
#include <lua.hpp> #include <string> #include <iostream> #include <sstream> #include <new> //. to use "placement new" //. Luaスクリプト側に公開したいクラス class Person { private: int m_age; std::string m_name; public: explicit Person(const int age = 0, const std::string name = "Unknown") : m_age(age), m_name(name) {} void ChangeName(std::string name) { m_name = name; } std::string Info() const { std::ostringstream stream; stream << "Name is " << m_name << ", Age is " << std::to_string(m_age); return stream.str(); } };
auto DefinePerson(lua_State *L) { static const luaL_Reg Factory[] = { { "new", [](lua_State *L) //. ラムダ式 { auto p = lua_newuserdata(L, sizeof(Person)); //. lua_newuserdataでアロケートしたメモリブロックへPersonのインスタンスを配置する(placement new)。 //. lua_newuserdataによって最上位のスタックにユーザーデータがPushされているため、 //. これに以外に2つのスタックが存在していれば、引数指定ありでnewをCallされたとみなす。 if (3 == lua_gettop(L)) { new(p) Person(luaL_checkinteger(L, -3), luaL_checkstring(L, -2)); } else { new(p) Person(); } //. スタックトップのオブジェクト(ユーザーデータ)のメタテーブルへ //. "person_type"に紐づくメタテーブルを設定。 luaL_setmetatable(L, "person_type"); return 1; //. 戻り値の数 } }, { nullptr, nullptr } }; static const luaL_Reg Methods[] = { { "ChangeName", [](lua_State *L) //. ラムダ式 { //. "person_type"に紐づくメタテーブルをもったユーザーデータかを検証し、Person*型へキャストして使用する。 reinterpret_cast<person>(luaL_checkudata(L, 1, "person_type"))->ChangeName( luaL_checkstring(L, -1) ); return 0; //. 戻り値の数 } }, { "Info", [](lua_State *L) //. ラムダ式 { //. "person_type"に紐づくメタテーブルをもったユーザーデータかを検証し、Person*型へキャストして使用する。 auto info = reinterpret_cast<person>(luaL_checkudata(L, 1, "person_type"))->Info(); lua_pushstring(L, info.c_str()); return 1; //. 戻り値の数 } }, { nullptr, nullptr } }; //. メタテーブルを作成し、キー名"person_type"としてレジストリに登録。 luaL_newmetatable(L, "person_type"); //. Methodsを割り当てたテーブルを作成。 luaL_newlib(L, Methods); //. メタテーブル内の__indexメタメソッドに、上記テーブルを割り当てる。 lua_setfield(L, -2, "__index"); //. メタテーブルをPop。 lua_pop(L, 1); //. Factoryメソッドを割り当てたテーブルを作成し、 //. 呼び出し元へ制御を戻す。 luaL_newlib(L, Factory); return 1; //. 戻り値の数 }
auto main() -> int { const char* fileName = "C:\\Develop\\LuaScript\\Sample.lua"; lua_State* L = luaL_newstate(); luaL_openlibs(L); luaL_requiref(L, "person", DefinePerson, 1); lua_pop(L, 1); if (luaL_dofile(L, fileName)) { printf("%s", lua_tostring(L, -1)); } lua_close(L); std::cin.get(); }
0 件のコメント:
コメントを投稿