Lua:查询一个userdata对象的metatable名称

我想查询某个对象的metatable名称。

假设我注册了一个metatable如下:

Object obj; // 一些 C 对象

luaL_newmetatable(lua, "my_metatable"); // 它是空的

lua_pushlightuserdata(lua, &obj);
luaL_setmetatable(lua, "my_metatable");
lua_setglobal(lua, "obj_");

文档 此处 表明 luaL_newmetatable 执行了双向关联,即 它把名称用作表的键并把表用作名称的键。 所以,有了这个知识,我认为我可以通过以下方式实现我的目标:

int getMTName(lua_State *L)
{
    lua_getmetatable(L, 1); // 获取对象的metatable
    lua_rawget(L, LUA_REGISTRYINDEX); // 由于metatable是键
                                      // 使用它来查询名称
    return 1; // 栈底现在是metatable名称
}

并像这样注册它:

lua_pushcfunction(lua, getMTName);
lua_setglobal(lua, "getMTName");

但是,不幸的是,它没有起作用,它返回了 nil。那么我做错了什么?

这里是完整的源代码(C++):

extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

#include <iostream>

struct Object {
    int x;
};

int getMTName(lua_State *L)
{
    lua_getmetatable(L, 1);
    lua_rawget(L, LUA_REGISTRYINDEX);
    return 1;
}

int main(int argc, char **argv)
{

    lua_State *L =luaL_newstate();
    luaL_openlibs(L);

    Object obj;

    lua_pushcfunction(L, getMTName);
    lua_setglobal(L, "getMTName");

    luaL_newmetatable(L, "my_metatable");

    lua_pushlightuserdata(L, &obj);
    luaL_setmetatable(L, "my_metatable");
    lua_setglobal(L, "obj_");

    int e = luaL_dostring(L, "print(getMTName(obj_))");

    if (e)
    {
        std::cerr << "ERR: " << lua_tostring(L, -1) << std::endl;
        lua_pop(L, 1);
    }

    return 0;

}

输出是 nil。我的 Lua 版本是 5.3。

点赞
用户2604712
用户2604712

好的,我明白了。查看 https://www.lua.org/source/5.3/lauxlib.c.html#luaL_newmetatable 中的源代码,我注意到这种双重关联是使用元表中的 "__name" 来完成的,而不是使用表作为其名称在注册表中的键。这种行为始于 Lua 5.3。

示例代码:

extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

#include <iostream>

struct Object {
    int x;
};

int getMTName(lua_State *L)
{
    lua_getmetatable(L, 1);
    lua_pushstring(L, "__name");
    lua_rawget(L, 2);
    return 1;
}

int main(int argc, char **argv)
{

    lua_State *L =luaL_newstate();
    luaL_openlibs(L);

    Object obj;

    lua_pushcfunction(L, getMTName);
    lua_setglobal(L, "getMTName");

    luaL_newmetatable(L, "my_metatable");

    lua_pushlightuserdata(L, &obj);
    luaL_setmetatable(L, "my_metatable");
    lua_setglobal(L, "obj_");

    int e = luaL_dostring(L, "print(getMTName(obj_))");

    if (e)
    {
        std::cerr << "ERR: " << lua_tostring(L, -1) << std::endl;
        lua_pop(L, 1);
    }

    return 0;

}
2016-08-13 11:56:46