Results 1 to 8 of 8

Thread: A cleaner/easier class idiom in Lua

  1. #1
    Lesser Knight
    Join Date
    Dec 2006
    Location
    United States
    Posts
    47

    Default A cleaner/easier class idiom in Lua

    Making a class using Lua is pretty gross and requires underlying knowledge about how metatables work and other nonsense. Anyway, I made a function to automate the process.

    Example:
    Code:
    Person = class()
    
    function Person:__init(name, age)
        self.name = name
        self.age = age
    end
    
    newPerson = Person("Bob", 41)
    Instead of defining a constructor like you normally would, class() creates one for you and then calls __init when the class is created. (Defining __init is optional though.) Also, you now just call the name of the class instead of explicitly calling the constructor, which is how it works in most OO languages. class() also takes an optional argument for a superclass from which to inherit from. (Right now there is not multiple-inheritance because that would take a lot more effort on my part. )

    Here's the definition for class():
    Code:
    function class(superclass, name)
        local cls = superclass and superclass() or {}
        cls.__name = name or ""
        cls.__super = superclass
        return setmetatable(cls, {__call = function (c, ...)
            self = setmetatable({__class = cls}, cls)
            if cls.__init then
                cls.__init(self, ...)
            end
            return self
        end})
    end
    I hope your brain didn't explode.

  2. #2
    Administrator Seth's Avatar
    Join Date
    Jul 2002
    Location
    Japan
    Posts
    5,343

    Default

    That's great, a lot easier on the eyes. Hope you don't mind if I steal it! I can make the intro_menu.lua simpler to look at.
    Seth A. Robinson
    Robinson Technologies

  3. #3
    Lesser Knight
    Join Date
    Dec 2006
    Location
    United States
    Posts
    47

    Default

    Quote Originally Posted by Seth View Post
    That's great, a lot easier on the eyes. Hope you don't mind if I steal it! I can make the intro_menu.lua simpler to look at.
    Er, the point of posting it was so anyone could use it. I hereby place it in the public domain.

    Also, I can add multiple inheritance and other stuff later. I also made an isinstance() function and an issubclass() function which I'll post later. (I'm not at my own computer at the moment.)

    P.S.

    Oh, and the name argument is mostly for introspection. (You sadly have to provide it explicitly which violates the DRY principle; there may be a way around this, but I doubt it.) I should probably add a __tostring metamethod to the metaclass (class function) as well for introspective purposes. I guess that would make it a meta-metamethod. Not to be confused with a meta-meta-method.
    Last edited by Ian; 01-24-2007 at 04:55 PM.

  4. #4
    Lesser Knight
    Join Date
    Dec 2006
    Location
    United States
    Posts
    47

    Default

    Code:
    function issubclass(class1, class2)
        while true do
            if class1 == class2 then
                return true
            end
            class2 = class2.__super
            if not class2 then
                break
            end
        end
        return false
    end
    
    function isinstance(inst, cls)
        return issubclass(inst.__class, cls)
    end

  5. #5
    Administrator Seth's Avatar
    Join Date
    Jul 2002
    Location
    Japan
    Posts
    5,343

    Default Calling Ian, the lua master

    Ok, I tried to convert my classes to use this new and improved system today but ran into a snag.

    Everything works fine, but MyObj:FuncName() syntax doesn't work anymore.

    Here's a test to illustrate:

    Code:
    //Ian's cool class wrapper to make faking class's easier.
    
    function class(superclass, name)
        local cls = {}
        cls.__name = name or ""
        cls.__super = superclass
        return setmetatable(cls, {__call = function (c, ...)
    		self = setmetatable({__index = cls}, cls)
             if cls.__init then
                cls.__init(self, ...)
            end
            return self
        end})
    end
    
    //here's an example/test using the class function
    
    Monster = class();  //create the class.
    
    //define the class function protypes
    function Monster:__init()
    	//if a function named __init exists, it will be called when an object is initialized (like a constructor)
    	LogMsg("A monster initted!");
    	self.hitpoints = 10;
    end
    
    function Monster:Hurt(damage)
    	self.hitpoints = self.hitpoints - damage;
    end
    
    //ok, now let's actually make some objects and use them.
    
    dave = Monster();
    tom = Monster();
    
    LogMsg("Dave has " .. dave.hitpoints .. " hitpoints.");
    LogMsg("Tom has " .. tom.hitpoints .. " hitpoints.");
    
    //dave:Hurt(2); //why can't I do this?
    dave.__index.Hurt(dave, 2); //long form
    //tom:Hurt(8); //why can't I do this?
    tom.__index.Hurt(tom, 8); //long form
    
    //show hitpoints again
    
    LogMsg("Dave has " .. dave.hitpoints .. " hitpoints.");
    LogMsg(" Tom has " .. tom.hitpoints .. " hitpoints.");
    I want to say dave:Hurt(2) but I have to do dave.__index.Hurt(dave, 2). Any ideas on fixing this?
    Seth A. Robinson
    Robinson Technologies

  6. #6
    Lesser Knight
    Join Date
    Dec 2006
    Location
    United States
    Posts
    47

    Unhappy The importance of testing

    Code:
    function class()
        local cls = {}
        cls.__index = cls
        return setmetatable(cls, {__call = function (c, ...)
            instance = setmetatable({}, cls)
            if cls.__init then
                cls.__init(instance, ...)
            end
            return instance
        end})
    end
    I stupidly put __index in the wrong table. It works now. (I'm also working on something that will have single, multiple, and diamond inheritance, cooperative methods and other stuff. I have a ton of unit tests this time.)

  7. #7
    Administrator Seth's Avatar
    Join Date
    Jul 2002
    Location
    Japan
    Posts
    5,343

    Default

    Thanks, it's working great now.
    Seth A. Robinson
    Robinson Technologies

  8. #8

    Default

    Quote Originally Posted by Ian View Post
    Code:
    function class()
        local cls = {}
        cls.__index = cls
        return setmetatable(cls, {__call = function (c, ...)
            instance = setmetatable({}, cls)
            if cls.__init then
                cls.__init(instance, ...)
            end
            return instance
        end})
    end
    I stupidly put __index in the wrong table. It works now. (I'm also working on something that will have single, multiple, and diamond inheritance, cooperative methods and other stuff. I have a ton of unit tests this time.)
    Sry for reply to this old topic, but this topic is one of the first google shows if you search for "class in lua" and i think i found a bug.
    If you create a class "ClassA" . And try to create a class "ClassB" directly in the __init of ClassA there is an error.
    I changed line 5 to "local instance = setmetatable({}, cls)" . Now it seems to work (I'm not an expert in lua, so i'm not really sure !).
    Feel free to use the code for whatever you like

    Code:
    function class()
        local cls = {}
        cls.__index = cls
        return setmetatable(cls, {__call = function (c, ...)
            local instance = setmetatable({}, cls)
            if cls.__init then
                cls.__init(instance, ...)
            end
            return instance
        end})
    end

Similar Threads

  1. An alternative, easier way to kill Mog
    By Random Terrain in forum Dink Smallwood HD
    Replies: 0
    Last Post: 04-26-2003, 07:45 PM

Bookmarks

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •