luaL_checkudata继承检查 [英] luaL_checkudata inheritance checking

查看:680
本文介绍了luaL_checkudata继承检查的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下情况:

从"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 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 Bstruct 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:

  1. Derived用户数据类型使用Base元表作为其 __index元方法,即派生的userdata类型不会不是 拥有自己的方法,因此没有自己的__index.
  2. 用于Base对象本身.
  1. A Derived userdata type uses the Base 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.
  2. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆