如何为Lua类实现具有__index函数的继承?

根据这个教程,Lua可以通过将元表分配给派生类表来实现继承,其中__index指向基类表,例如:

BaseClass = {}
function BaseClass:foo()
end

DerivedClass = {}
setmetatable(DerivedClass, {__index=BaseClass}) -- 在这里设置继承

derived_instance = {}
setmetatable(derived_instance, {__index=DerivedClass})
derived_instance:foo() -- derived_instance可以调用BaseClass的foo()方法

然而,在Lua中也可以通过元表__index指向函数来实现面向对象编程。 这提供了更好的灵活性,但我不知道如何实现继承。这段测试代码演示了我的想法,但它无法运行:

#include <juce_core/juce_core.h>
#include <lauxlib.h>
#include <lualib.h>

int BaseClass_foo(lua_State * lua)
{
    juce::Logger::writeToLog("BaseClass_foo called");
    return 0; 
}

int BaseClass(lua_State * lua)
{
    juce::Logger::writeToLog("BaseClass called");
    const char * key = lua_tostring(lua, 2);
    if (strcmp(key, "foo") == 0)
        lua_pushcfunction(lua, BaseClass_foo);
    else
        lua_pushnil(lua);
    return 1;
}

int DerivedClass(lua_State * lua)
{
    juce::Logger::writeToLog("DerivedClass called");
    lua_pushnil(lua);
    return 1;
}

const char * lua_src =
    "obj = {}\n"
    "setmetatable(obj, {__index=DerivedClass})\n"
    "obj:foo()";

int main()
{
    lua_State * lua = luaL_newstate();
    luaL_openlibs(lua);

    lua_pushcfunction(lua, BaseClass);
    lua_setglobal(lua, "BaseClass");
    lua_pushcfunction(lua, DerivedClass);
    lua_setglobal(lua, "DerivedClass");

    // 通过元表设置继承
    lua_getglobal(lua, "DerivedClass");

    lua_createtable(lua, 0, 0);
    lua_pushstring(lua, "__index");
    lua_getglobal(lua, "BaseClass");
    lua_settable(lua, -3);

    lua_setmetatable(lua, -2);

    // 运行lua代码
    int load_re = luaL_loadstring(lua, lua_src);
    if (load_re == LUA_OK)
        lua_call(lua, 0, 0);
    else
        juce::Logger::writeToLog("Lua source load failed with " + juce::String(load_re) + ": " + lua_tostring(lua, -1));

    lua_close(lua);
}

结果,Lua声称一个错误,即您不能在nil值上调用foo,根本没有调用BaseClass函数。元索引搜索链在这里被中断,与表实现的类系统不同,Lua自动搜索BaseClass表,如果在DerivedClass表中不存在键,则继续搜索。有没有办法让Lua继续解释绑定到DerivedClass的元表?

点赞
用户2858170
用户2858170

如果有人索引 DerivedClass.foo,那么 __index 函数必须返回 BaseClass.foo

如果 __index 是一个表格,你可以这样做:

getmetatable(DerivedClass).__index:foo()

这归结为 BaseClass:foo()

如果 __index 是一个函数,你可以这样做:

getmetatable(DerivedClass).__index(DerivedClass, "foo")()

因此,你的 __index 函数必须为键参数 "foo" 返回 BaseClass.foo,才能产生相同的效果。

2021-07-14 09:05:20