luaL_checkudata继承检查 [英] luaL_checkudata inheritance checking
问题描述
我有以下情况:
从"Base"派生的一个名为"Base"的类和另一个名为"Derived"的类.我使用元表来支持Lua中的继承.但是,我需要一种解决方案来检查传递的userdata是否从指定的类继承.
One class named "Base" and second class named "Derived" which derives from "Base". I use metatables to support inheritance in Lua. However I need a solution for checking if passed userdata inherits from specified class.
static int BaseGetMyVar(lua_State *state)
{
Base* base = *(Base**)luaL_checkudata(state, 1, "Base");
//Base* base = *(Base**)lua_touserdata(state, 1);
lua_pushinteger(state, base->GetMyVar());
return 1;
}
当我将派生的" udata传递给此方法时,我得到:
When I pass "Derived" udata to this method I get:
HelloWorld.lua:17: calling 'getMyVar' on bad self (Base expected, got userd
ata)
如何检查继承?这样我就可以通过Base或Derived.
How do I check inheritance? So I can pass either Base or Derived.
我发现的当前解决方案:
Current solutions I've found:
http://lua-users.org/lists/lua-l/2005-06/msg00000.html
http://lua-users.org/lists/lua-l/2007-04/msg00324.html
https://github.com/diegonehab/luasocket/blob/master/src/auxiliar.c
siffiejoe的提案(谢谢)
Proposal by siffiejoe(Thanks)
https://github.com/siffiejoe/lua-moon#moon_defcast
另一个偶然发现的解决方案:
Another solution found by accident:
http://lua-users.org/lists/lua-l/2013-06/msg00492.html
我自己的解决方案(我喜欢拥有东西)
void* luaL_checkclass(lua_State *L, const char *classname, int objidx)
{
if(!luaL_getmetafield(L, objidx, "__index"))
return NULL;
lua_getfield(L, -1, "__name");
if(lua_type(L, -1) != LUA_TSTRING)
return NULL;
char const* basename = lua_tostring(L, -1);
lua_pop(L, 2);
if(strcmp(basename, classname) != 0)
luaL_typeerror(L, objidx, classname);
return lua_touserdata(L, objidx);
}
我想出了自己的主意.这要求metatable具有字段__name(Lua 5.3实现了该字段,但是您仍可以在以前的版本中自行添加此字段).
I came up with my own idea. This requires metatable to have field __name(Lua 5.3 implements this, but you can still add this field on your own in previous versions).
更详细:
我在Lua中注册的每个元表都拥有__index字段,该字段要么分配给self,要么如果它具有基类,则将其分配给基类. __index的表拥有__name字段,这是类的名称...我们可以只比较名称.
Each metatable I register in Lua owns __index field which is either assigned to self or if it has a base class it is assigned to base class. The table at __index owns __name field which is the name of the class... we can just compare the names.
如果现在将Derived对象传递给Base方法,它将可以正常工作.
If we pass Derived object to Base method now it will work just fine.
推荐答案
如果要对多个相关用户数据重新使用lua_CFunction
类型,您必须克服两个问题:
If you want to re-use a lua_CFunction
for multiple related userdata
types, you have to overcome two problems:
通常用于以下用途的 luaL_checkudata()
函数
对用户数据的内存进行类型安全的访问仅检查一种特定的
用户数据类型.因此,所有提到的方法都使用不同的方法
函数来检查lua_CFunction
的参数.
The luaL_checkudata()
function that is normally used for
type-safe access to a userdata's memory only checks for one specific
userdata type. All mentioned approaches therefore use a different
function to check the arguments of the lua_CFunction
.
- http://lua-users.org/lists /lua-l/2005-06/msg00000.html
提出了一个聪明的命名约定,以便用户数据类型
假定
"A.B.C"
也是有效的"A.B"
用户数据,并且 有效的"A"
用户数据.检查只是比较 具有请求的用户数据类型的实型名称的前缀 名称. - http://lua-users.org/lists /lua-l/2007-04/msg00324.html
实现一个基本上调用
luaL_checkudata()
的函数 多个指定的类型名称,直到其中一个调用 成功. -
https://github.com/diegonehab/luasocket/blob/master/src/auxiliar.c (和 https://github.com/siffiejoe/lua-moon#moon_defcast 以及在userdata的数据库中维护一组受支持的类型名称 元表.如果请求的类型不完全匹配,则为 只是一个额外的表查询,以检查类型是否为 应该兼容.
- http://lua-users.org/lists/lua-l/2005-06/msg00000.html
proposes a clever naming convention so that a userdata of type
"A.B.C"
is assumed to also be a valid"A.B"
userdata, and a valid"A"
userdata. Checking is just a matter of comparing a prefix of the real type name with the requested userdata type name. - http://lua-users.org/lists/lua-l/2007-04/msg00324.html
implements a function that basically calls
luaL_checkudata()
for multiple specified type names until one of those calls succeeds. https://github.com/diegonehab/luasocket/blob/master/src/auxiliar.c (and https://github.com/siffiejoe/lua-moon#moon_defcast as well) maintain a set of supported type names in the userdata's metatable. If the requested type doesn't match exactly, it's just an additional table lookup to check whether the type is supposed to be compatible.
这也是@ Rochet2的答案中使用的方法
问题,还有一个附加的问题:它重新使用链接的__index
元方法表,以使兼容类型集更易于管理.
This is also the approach used in @Rochet2's answer to this
question, with an additional twist: It re-uses the chained __index
metamethod tables to make the set of compatible types more manageable.
即使当一个用户数据属于一组相关的用户数据类型时, 不同类型的内存布局通常略有不同 (否则,使用不同的用户数据类型没有多大意义 完全没有).因此,第二个问题是获取兼容的指针 带有请求的用户数据类型.
Even when a userdata belongs to a group of related userdata types, the memory layout of the different types is usually slightly different (otherwise it wouldn't make much sense to use different userdata types at all). So the second problem is to get a pointer that is compatible with the requested userdata type.
上面提到的大多数方法都忽略了此问题,因为
C,如果您谨慎定义自己的定义,就可以摆脱它
相关的用户数据类型. C标准保证指向a的指针
struct
与指向其第一个成员的指针具有相同的值.因此,如果
你有
Most of the approaches mentioned above ignore this problem because in
C you can get away with it if you are careful how you define your
related userdata types. The C standard guarantees that a pointer to a
struct
has the same value as a pointer to its first member. So if
you have
struct A { ... };
struct B { A a; ... };
struct C { B b; ... };
指向struct C
的指针自动也是指向的有效指针
struct B
和struct A
.
a pointer to struct C
is automatically also a valid pointer to
struct B
and struct A
.
但是,如果您不能保证这种特定的内存布局,或者 您将C ++与多重继承或虚拟继承一起使用,或仅 虚拟方法的不幸选择(请参见此SO答案), 您可能必须调整指针值以使其有效为 指向相关userdata类型的指针.
However if you cannot guarantee this particular memory layout, or if you use C++ with multiple inheritance, or virtual inheritance, or just an unfortunate choice of virtual methods (see this SO answer), you'll likely have to adjust the pointer values to make them valid as pointers to a related userdata type.
https://github.com/siffiejoe/lua-moon#moon_defcast
存储简单的回调函数,可用于调整
指向所需类型的指针值.例如.对于class B : public A ...
如果p
确实是void
,则可以使用return (void*)(A*)(B*)p;
指向B
对象的指针,该对象应传递给以下函数
需要void
指向A
对象的指针.
https://github.com/siffiejoe/lua-moon#moon_defcast
stores simple callback functions that you can use to adjust the
pointer value to the required type. E.g. for class B : public A ...
you would use return (void*)(A*)(B*)p;
if p
really is a void
pointer to a B
object that should be passed to a function that
expects a void
pointer to an A
object.
添加到问题中的方法忽略了问题2.它
尝试解决问题1(识别兼容的用户数据类型)
通过将用户数据类型名称存储在元表中(该表也可以
作为__index
元方法).在Lua 5.3中, luaL_newmetatable()
函数将为您执行此操作.给定的代码段仅适用于
两种情况:
The approach added in an edit to the question ignores problem 2. It
tries to solve problem 1 (identification of compatible userdata types)
by storing the userdata type name in the metatable (which also serves
as the __index
metamethod). In Lua 5.3 the luaL_newmetatable()
function will do this for you. The given code snippet only works for
two cases:
-
Derived
用户数据类型使用Base
元表作为其__index
元方法,即派生的userdata类型不会不是 拥有自己的方法,因此没有自己的__index
. - 用于
Base
对象本身.
- A
Derived
userdata type uses theBase
metatable as its__index
metamethod, i.e. the derived userdata type does not have methods on its own, and so doesn't have its own__index
. - For the
Base
object itself.
如果您添加更多级别(例如MoreDerived
类型),或者您需要
特定于Derived
类型的方法,这种简单方法不会
工作了.您必须走__index
元方法链
向上(假设继承是使用链接的__index
处理的
元方法),直到找到所需的类型名称为止.
If you add more levels (e.g. a MoreDerived
type), or if you need
methods specific to the Derived
type, this simple approach won't
work anymore. You'd have to walk the __index
metamethods chain
upward (assuming inheritance is handled using chained __index
metamethods) until you find the type name you are looking for.
这篇关于luaL_checkudata继承检查的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!