在以下示例中,创建了userdata
类型MyType
的值,并创建了一个带有调用__tostring
的元函数LI_MyType__tostring
的表格。该代码创建了一个基于闭包的lua OOP。我对提供的示例的抱怨似乎只有一种方法可以通过upvalues将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的“普通”metatable样式的情况类似,但我想保持封闭性并避免引入冒号语法。
另一种提出这个问题的方法是,有没有办法在使用基于闭包的OOP时跨userdata
个实例共享元表?从脚本方面使用lua的语法,我不认为这是可能的,但我希望在C方面可以做一些事情。
更新(2013-10-10):基于@lhf's answer to use lua_setuservalue()
and lua_getuservalue()
我已经确定的协议允许我重复使用metatables:
luaL_newmetatable()
注册单个元表对象。此metatable现在可以在userdata
个实例之间共享,因为在注册metatable时没有使用upvalues。userdata
值(lua_newuserdata()
)。userdata
值(lua_setmetatable()
)。userdata
创建并填充实例方法调用/属性表。lua_setuservalue()
上使用userdata
来存储对每个实例属性/方法表的引用。__index
)以使用userdata
的用户值表。结果:
仍然无法为每个用户数据创建一个方法/属性表,但这种开销是名义上的。如果obj.myMethod()
在不使用obj
的情况下以某种方式将function myMethod()
传递给:
会很不错,但这正是:
所做的,因为这是不可能的另一种方式(除非你确实使用了一个upvalue)。
答案 0 :(得分:1)
lua_setuservalue
似乎正是您所需要的。当然也有lua_getuservalue
。
(我正在跳过C ++代码并回答标题中的问题。)
答案 1 :(得分:0)
由于某些原因,我认为你不应该完全这样做。
从插槽1中获取对象,并检查其类型是否与您的用户数据匹配。 (luaL_checkudata)
如果它不是一个对象和tostring被调用,例如,只输出它的一类对象名,而不是实例细节。它更有意义,如果对象报告它实际上是什么,可能会使调试更简单,而不是试图过于聪明和误导你。