5

次の例でuserdataは、型の値が作成され、を呼び出すMyTypeメタ関数でテーブルが作成されます。このコードは、クロージャ ベースの lua OOP を作成します。提供された例に対する私の不満は、upv​​alues を介してメソッド呼び出しに関連付ける方法が 1 つしかないように見えることです。インスタンス間で同じメタテーブルを共有したい場合を除き、それ自体は問題ありません。__tostringLI_MyType__tostringuserdata

理想的な世界では、そして私がこの質問で明らかにしたいと思っていることですが、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()、メタテーブルを再利用できるように解決したプロトコルに基づいて、次のようになります。

  1. を使用して単一のメタテーブル オブジェクトを登録しますluaL_newmetatable()userdataメタテーブルの登録時に上位値が使用されないため、このメタテーブルはインスタンス間で共有できるようになりました。
  2. 値を作成しuserdataます ( lua_newuserdata())。
  3. 正しいメタテーブルをuserdata値に割り当てます ( lua_setmetatable())。
  4. インスタンス メソッド呼び出し/属性テーブルを作成し、1 つの上位値であるuserdata.
  5. lua_setuservalue()onを使用しuserdataて、インスタンスごとの属性/メソッド テーブルへの参照を格納します。
  6. の uservalue テーブル__indexを使用するように、さまざまなメタメソッド ( など) を変更します。userdata

結果として:

  • 上位値はメタメソッドでは使用されません
  • upvalues は、値のインスタンス メソッドでのみ使用されます
  • 特定のクラスのインスタンスごとに 1 つの追加テーブルしかありません

ユーザーデータごとにメソッド/属性テーブルの作成を回避することはまだ不可能ですが、そのオーバーヘッドはわずかです。を使用せずに何らかの形でobj.myMethod()渡せばいいのですが、これは別の方法では不可能なためです (upvalue を使用しない限り)。objfunction myMethod()::

4

2 に答える 2

2

lua_setuservalueまさにあなたが必要としているようです。ももちろんありlua_getuservalueます。

(C++ コードは飛ばして、タイトルの質問に答えます。)

于 2013-10-09T12:20:19.250 に答える
0

いくつかの理由から、正確にやろうとするべきではないと思います。

  1. object.method() を呼び出し、作成されたインスタンスからオブジェクトを推測しようとすると、指定されたオブジェクトで動作する関数ポインターを渡す機能がブロックされます。
  2. ガベージ コレクションが行われないオブジェクトへの循環参照があります (各インスタンスの関数がそのインスタンスを指し示します)。

スロット 1 からオブジェクトを取得し、そのタイプがユーザーデータと一致することを確認します。(luaL_checkudata)

たとえば、オブジェクトではなく tostring が呼び出された場合、インスタンスの詳細ではなく、オブジェクト名のクラスを出力するだけです。それははるかに理にかなっていて、オブジェクトが実際に何であるかを報告する場合は、賢すぎて誤解を招くよりもデバッグが簡単になる可能性があります.

于 2013-10-09T08:34:48.440 に答える