次の例でuserdata
は、型の値が作成され、を呼び出すMyType
メタ関数でテーブルが作成されます。このコードは、クロージャ ベースの lua OOP を作成します。提供された例に対する私の不満は、upvalues を介してメソッド呼び出しに関連付ける方法が 1 つしかないように見えることです。インスタンス間で同じメタテーブルを共有したい場合を除き、それ自体は問題ありません。__tostring
LI_MyType__tostring
userdata
理想的な世界では、そして私がこの質問で明らかにしたいと思っていることですが、upvalue をuserdata
介して関数呼び出しに関連付けずに、upvalue を値 (たとえば ) に関連付ける方法はありますか? クロージャーベースの lua OOPを引き続き使用し、インスタンス間で同じメタテーブルを共有できるトリックがあることを願っています。私は楽観的ではありませんが、誰かに提案や自明ではないトリックがあるかどうかを尋ねたいと思いました。
using FuncArray = std::vector<const ::luaL_Reg>;
static const FuncArray funcs = {
{ "__tostring", LI_MyType__tostring },
};
int LC_MyType_newInstance(lua_State* L) {
auto userdata = static_cast<MyType*>(lua_newuserdata(L, sizeof(MyType)));
new(userdata) MyType();
// Create the metatable
lua_createtable(L, 0, funcs.size()); // |userdata|table|
lua_pushvalue(L, -2); // |userdata|table|userdata|
luaL_setfuncs(L, funcs.data(), 1); // |userdata|table|
lua_setmetatable(L, -2); // |userdata|
return 1;
}
int LI_MyType__tostring(lua_State* L) {
// NOTE: Blindly assume that upvalue 1 is my userdata
const auto n = lua_upvalueindex(1);
lua_pushvalue(L, n); // |userdata|
auto myTypeInst = static_cast<MyType*>(lua_touserdata(L, -1));
lua_pushstring(L, myTypeInst->str()); // |userdata|string|
return 1; // |userdata|string|
}
次のようなことを実行する方法があることを願っています (これは疑似コードです!):
// Assume that arg 1 is userdata
int LI_MyType__tostring(lua_State* L) {
const int stackPosition = -1;
const int upvalueIndex = 1;
const auto n = lua_get_USERDATA_upvalue(L, stackPosition, upvalueIndex);
lua_pushvalue(L, n); // |userdata|
auto myTypeInst = static_cast<MyType*>(lua_touserdata(L, -1));
lua_pushstring(L, myTypeInst->str()); // |userdata|string|
return 1; // |userdata|string|
}
これは、OOP の「通常の」メタテーブル スタイルの場合と似ていることはわかっていますが、クロージャ ベースを維持し、コロン構文の導入を避けたいと考えています。
この質問をする別の方法はuserdata
、クロージャーベースの OOP を使用しているときにインスタンス間でメタテーブルを共有する方法はありますか? スクリプト側から lua の構文を使用すると、私はそれが可能だとは思いませんが、C 側で何かできることを望んでいます。
更新(2013-10-10):使用する @lhf の回答lua_setuservalue()
とlua_getuservalue()
、メタテーブルを再利用できるように解決したプロトコルに基づいて、次のようになります。
- を使用して単一のメタテーブル オブジェクトを登録します
luaL_newmetatable()
。userdata
メタテーブルの登録時に上位値が使用されないため、このメタテーブルはインスタンス間で共有できるようになりました。 - 値を作成し
userdata
ます (lua_newuserdata()
)。 - 正しいメタテーブルを
userdata
値に割り当てます (lua_setmetatable()
)。 - インスタンス メソッド呼び出し/属性テーブルを作成し、1 つの上位値である
userdata
. lua_setuservalue()
onを使用しuserdata
て、インスタンスごとの属性/メソッド テーブルへの参照を格納します。- の uservalue テーブル
__index
を使用するように、さまざまなメタメソッド ( など) を変更します。userdata
結果として:
- 上位値はメタメソッドでは使用されません
- upvalues は、値のインスタンス メソッドでのみ使用されます
- 特定のクラスのインスタンスごとに 1 つの追加テーブルしかありません
ユーザーデータごとにメソッド/属性テーブルの作成を回避することはまだ不可能ですが、そのオーバーヘッドはわずかです。を使用せずに何らかの形でobj.myMethod()
渡せばいいのですが、これは別の方法では不可能なためです (upvalue を使用しない限り)。obj
function myMethod()
:
: