jiandingzhe
jiandingzhe

Reputation: 2121

How to implement inheritance for Lua classes whose __index is a function?

By this tutorial, Lua can implement inheritance by assigning a metatable to the derived class table whose __index points to the base class table, like this:

BaseClass = {}
function BaseClass:foo()
end

DerivedClass = {}
setmetatable(DerivedClass, {__index=BaseClass}) -- set inheritance here

derived_instance = {}
setmetatable(derived_instance, {__index=DerivedClass})
derived_instance:foo() -- derived_instance can call BaseClass method foo()

However, object-oriented in Lua can also be implemented via metatable __index pointing to a function. This provides better flexibility, but I don't know how to implement inheritance. This test code demonstrates my idea but it fails to run:

#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" );

    // set inheritance by metatable on class function
    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 );

    // run lua code
    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 );
}

In result, Lua claims an error that you cannot call foo on a nil value, the BaseClass function is not called at all. The meta-index search chain is broken here, different from the table-implemented class system that Lua automatically search BaseClass table if a key does not exist in DerivedClass table. Is there any way to have Lua continue to interpret the metatable binded to DerivedClass?

Upvotes: 1

Views: 461

Answers (1)

Piglet
Piglet

Reputation: 28940

Well that __index function would have to return BaseClass.foo if someone indexes DerivedClass.foo.

If __index is a table you basically do this:

getmetatable(DerivedClass).__index:foo()

which boils down to BaseClass:foo()

If __index is a function you do this:

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

So your __index function must return BaseClass.foo for key argument "foo" to have the same effect.

Upvotes: 1

Related Questions