Question

My goal is to have a standard way of creating classes with full-scaled multiple inheritance and the ability to not only inherit to other classes but also to instances of themselves via the new()-constructor. If I call a missing value from the class or from its object and this value is a function, I would like to have it stored into the class for later use but not in the object/instance (performance issues when implementing the same function in massive amoutns of istances of a class).

At the moment, I am using a createClass(...)-function very similar to the one mentioned in the official tutorial series (http://www.lua.org/pil/16.3.html) to create classes that inherit from almost any number of parent classes:

-- look up for k in list of tables plist
local function search (k, plist)
    for i=1, #plist do
        local v = plist[i][k]     -- try i-th superclass
        if v then return v end
    end
end

local function createClass (...)
    local args = {...};
    local c = {};
    -- search all provided parent classes for variables/functions to inherit and include them
    -- could be done without including them (deeply nested inheritance might be an issue this way
    -- due to repeated search calls for each use of an inherited function)
    setmetatable(c, {__index = function (t, k)
        local v = search(k, args);
        t[k] = v;
        print(t, " ...looking for... ", k, v);
        return v;
    end})
    return c;
end

It passes an index-metamethod to the newly created class that searches all parents for the specified missing key and stores it for later use in the calling class. This works as intendet, even when searching a huge nested inheritance hierarchy. Now, let us introduce a simple class with a basic constructor:

local P = {};
local MyClass = P;

function P.new ()
    local self = {};
    local priv = {};
    setmetatable(self, {__index = function (t, k)
        -- shouldn't this invoke __index-metamethod of P because P does not already have k
        -- at this stage of the program?
        local v = P[k];
        print(t, " ...looking for ... ", k, v);
        if(type(v) == "function") then
            -- ??? maybe do somethine else here to enforce __index of class P
        else
            t[k] = v;
        end
        return v;
    end});

    self.testSelf = function () print("testSelf") end;
    priv.testPriv = "testPriv!";

    function self.accessPriv ()
        return priv;
    end

    return self;
end

function P.TEST ()
    print("calling TEST");
end

If we call this inheritance and class implementations with the following code, then we see that the __index-metamethod of the class is not invoked when a new instance of the class is created (compare the outputted table addresses), even though the __index-metamethod of the object requests the missing value/function from its (parent-)class which does not have such a value/function at this point. Apprently, this descripancy does not invoke the __index-metamethod of the class itself which confuses me. I would assume that it is always invoked if missing value of a class/table is requested, no matter from which part of the script.

  1. Why is the __index-metamethod of MyClass not invoked in the given example?
  2. And how can I accomplish it (by making as less changes of the code as possible)?

Your suggestions are welcomed!

Was it helpful?

Solution

I found the answer without understanding it completely. It appears, when calling the constructor of the class new, it is requires to pass the class itself to it. This way I can directly manipulate the self-variable which represents the class. There still remains confusion about why I am able to inherit from the class and its parents to the class instance/object while not being able to access the class for any other operation in objetcts __index-metamethod.

This class definition finally does the job:

local P = {};
local MyClass = P;

-- must pass class itself while creating new object of it (don't know why...)
function P:new ()
    local pub = {};
    local priv = {};
    setmetatable(pub, {__index = function (t, k)
        local v = P[k];
        -- store functions into the class for later use (less duplicates)
        -- and all other inherited variables into the objects itself
        if (type(v) == "function")
            self[k] = v;
        else
            t[k]    = v;
        end
        return v;
    end});

    pub.testSelf = function () print("testSelf") end;
    priv.testPriv = "testPriv!";

    function pub.accessPriv ()
        return priv;
    end

    return pub;
end

function P.TEST ()
    print("calling TEST");
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top