在lua中有一种方法可以将upvalue绑定到userdata值而不是函数吗?

Sea*_*ean 5 c++ lua metatable

在以下示例中,将userdata创建一个类型的值,MyType并使用__tostring调用的元函数创建一个表LI_MyType__tostring.该代码创建了一个基于闭包的lua OOP.我对提供的示例的抱怨似乎只有一种方法可以userdata通过upvalues与方法调用相关联.除非我想跨实例共享相同的元表,否则这本身并不成问题.

在一个理想的世界 - 以及我希望用这个问题发掘的东西 - 有没有办法将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|
}
Run Code Online (Sandbox Code Playgroud)

我希望有一种方法可以执行类似的操作(这是伪代码!):

// 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|
}
Run Code Online (Sandbox Code Playgroud)

我知道这与OOP的"普通"metatable样式的方式类似,但我想保持闭包并避免引入冒号语法.

问这个问题的另一种方法是,有没有办法userdata在使用基于闭包的OOP时跨实例共享元表?从脚本方面使用lua的语法,我不认为这是可能的,但我希望在C方面可以做些事情.


更新(2013-10-10):基于@ lhf的使用答案lua_setuservalue()以及lua_getuservalue()我已经确定的允许我重用metatables的协议是:

  1. 使用注册单个metatable对象luaL_newmetatable().此metatable现在可以跨userdata实例共享,因为在注册metatable时不使用upvalues.
  2. 创建一个userdata值(lua_newuserdata()).
  3. 将正确的元表分配给userdata值(lua_setmetatable()).
  4. 使用一个upvalue创建并填充实例方法调用/属性表userdata.
  5. 使用lua_setuservalue()userdata存储对每个实例属性/方法表的引用.
  6. 更改各种元方法(例如__index)以使用用户userdata值表.

作为结果:

  • upvalues从未在metamethods中使用
  • upvalues仅用于值的实例方法
  • 每个给定类的实例只有一个额外的表

仍然无法逃脱为每个用户数据创建方法/属性表,但这种开销是名义上的.如果不使用obj.myMethod()objfunction myMethod()某种方式传递会很好:,但这正是:因为这是不可能的另一种方式(除非你确实使用了upvalue).

lhf*_*lhf 2

lua_setuservalue似乎正是您所需要的。当然还有lua_getuservalue

(我跳过 C++ 代码并回答标题中的问题。)