主に必要な手続きは以下。
- 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 件のコメント:
コメントを投稿