在下面的示例userdata
中,创建了一个类型的值,并使用调用MyType
的元函数创建了一个表。该代码创建了一个基于闭包的 lua OOP。我对提供的示例的抱怨是,似乎只有一种方法可以通过 upvalues 与方法调用关联。就其本身而言,这不是问题,除非我想跨实例共享相同的元表。__tostring
LI_MyType__tostring
userdata
在一个理想的世界中——以及我希望通过这个问题挖掘出的东西——有没有办法将一个上值与一个值(例如userdata
)相关联,而不用通过一个上值将它与函数调用相关联?我希望有一个技巧可以让我继续使用基于闭包的 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()
)。 - 创建并使用一个上值填充实例方法调用/属性表,即
userdata
. - 使用
lua_setuservalue()
onuserdata
存储对每个实例属性/方法表的引用。 - 更改各种元方法(例如
__index
)以使用userdata
的用户值表。
作为结果:
- 元方法中从不使用 upvalues
- upvalues 仅用于值的实例方法中
- 给定类的每个实例只有一个额外的表
仍然不可能逃避为每个用户数据创建方法/属性表,但这种开销是名义上的。如果在不使用的情况下以某种方式obj.myMethod()
传递会很好,但这正是这样做的,因为这是不可能的另一种方式(除非你确实使用了上值)。obj
function myMethod()
:
: