Classy - Middleclass Inspired, But a lot faster

Showcase your libraries, tools and other projects that help your fellow love users.
User avatar
Forgemistress
Prole
Posts: 9
Joined: Thu Dec 01, 2016 1:50 am

Re: Classy - Middleclass Inspired, But a lot faster

Post by Forgemistress »

Update has been made.
The flame cleanses all. It clears away the old to make way for the new. Forge for yourself new opportunities from the dross of the old.
User avatar
DanielPower
Citizen
Posts: 50
Joined: Wed Apr 29, 2015 5:28 pm

Re: Classy - Middleclass Inspired, But a lot faster

Post by DanielPower »

I'm porting my engine to use Classy rather than Middleclass in a separate branch so I can test the difference. But I'm running into an issue.

Middleclass syntax:

Code: Select all

local Person = class('Person')
Person.species = 'Human'
Person.numArms = 2
Person.numFingers = 10
Person.defaultPicture = love.graphics.newImage('photo.jpg')
Classy syntax:

Code: Select all

local Person = class('Person', {
    species = 'Human',
    numArms = 2,
    numFingers = 10,
    defaultPicture = love.graphics.newImage('photo.jpg'), -- Causes error
}
First of all, I perfer the Middleclass syntax for initializing a class, however I can get over this for the performance boost. The issue I'm running into is that while middleclass will handle an Image or a Quad as a class variable just fine, Classy returns this error:

Code: Select all

Error: libraries/classy/footable.lua:111: [string "return {restQuad=Quad: 0x025dd3e0}"]:1: '<name>' expected near '0x025dd3e0'
stack traceback:
        [C]: in function 'assert'
        libraries/classy/footable.lua:111: in function '_makeAllocatorFunction'
        libraries/classy/classy.lua:213: in function 'class'
        class/unit/unit.lua:1: in main chunk
        [C]: in function 'require'
        main.lua:29: in main chunk
        [C]: in function 'require'
        [string "boot.lua"]:429: in function <[string "boot.lua"]:275>
        [C]: in function 'xpcall'
Am I doing something wrong, or is this an issue with Classy?
User avatar
Forgemistress
Prole
Posts: 9
Joined: Thu Dec 01, 2016 1:50 am

Re: Classy - Middleclass Inspired, But a lot faster

Post by Forgemistress »

The template that you provide to the class only defines the sets of keys that the underlying table will have as well as values that are automatically converted into strings. In this instance, defaultPicture is not the value you expect it to be since it is a love2d class object. You will want to instead assign this value via the love module functions in the classes __init__ function (its constructor).

This is something that I covered in my opening post. The template values are simply that, values. References are not supported.

You want something like this.

Code: Select all

-- Class definition ommited.
function Person:__init__()
    self.defaultPicture = love.graphics.newImage ('person.jpg')
end

-- Alternatively if you want to save memory for something like this.
Person.static.defaultPicture = love.graphics.newImage('person.jpg')

-- And then...
function Person:__init__(image)
    if image then
        self.image = love.graphics.newImage (image)
    else
        self.image = Person.defaultPicture
    end
end 
Under the hood, the module footable takes the template you provide, converts it into a string, then wraps it into a special function that it then compiles using dostring and stores in the class definition (I believe it stores it under <classname>.allocator, I'll need to double check). However, this is only done in <classname>:finalize, hence why that function is basically the linchpin of the entire implementation and is the reason for the performance I was able to squeeze out of it.

In cases of inheritance, the tables from the superclass is allocated and then merged with the definition of the subclass (with any collisions throwing out an error). Then another new allocator function is created using that merged table. When a class has its *:new function called, it invokes this allocator function that was compiled by footable and then does a whole bunch of metatable shenanigans before it returns to you the class instance that you then use.

The nice thing about this is that it also allows you to treat class instances as if they are normal tables. If memory serves, you should be able to do a 'pairs' Iteration on a class instance to see the keys and values that are stored as well as pass it into a library like binser to serialize it without any special modification (though my own personal copy includes templating and a serialization/deserialization utility that wraps binser, json, and lume into encoding functions because I'm secretly a masochist). The functions of the class are omitted from the pairs Iteration because they are part of the instance's __class__ key, though you could iterate those as well using instance.__class__.methods (which is subject to change). The static value I showed in my example would likewise be stored under instance.__class__.static. __class__ itself is part of the instances metatable, which the instance is set to __index into per standard lua metatable conventions.
The flame cleanses all. It clears away the old to make way for the new. Forge for yourself new opportunities from the dross of the old.
Post Reply

Who is online

Users browsing this forum: No registered users and 2 guests