How to create a class, subclass and properties in Lua? - class
I'm having a hard time grokking classes in Lua. Fruitless googling led me to ideas about meta-tables, and implied that third-party libraries are necessary to simulate/write classes.
Here's a sample (just because I've noticed I get better answers when I provide sample code):
public class ElectronicDevice
{
protected bool _isOn;
public bool IsOn { get { return _isOn; } set { _isOn = value; } }
public void Reboot(){_isOn = false; ResetHardware();_isOn = true; }
}
public class Router : ElectronicDevice
{
}
public class Modem :ElectronicDevice
{
public void WarDialNeighborhood(string areaCode)
{
ElectronicDevice cisco = new Router();
cisco.Reboot();
Reboot();
if (_isOn)
StartDialing(areaCode);
}
}
Here is my first attempt to translate the above using the technique suggested by Javier.
I took the advice of RBerteig. However, invocations on derived classes still yield: "attempt to call method 'methodName' (a nil value)"
--Everything is a table
ElectronicDevice = {};
--Magic happens
mt = {__index=ElectronicDevice};
--This must be a constructor
function ElectronicDeviceFactory ()
-- Seems that the metatable holds the fields
return setmetatable ({isOn=true}, mt)
end
-- Simulate properties with get/set functions
function ElectronicDevice:getIsOn() return self.isOn end
function ElectronicDevice:setIsOn(value) self.isOn = value end
function ElectronicDevice:Reboot() self.isOn = false;
self:ResetHardware(); self.isOn = true; end
function ElectronicDevice:ResetHardware() print('resetting hardware...') end
Router = {};
mt_for_router = {__index=Router}
--Router inherits from ElectronicDevice
Router = setmetatable({},{__index=ElectronicDevice});
--Constructor for subclass, not sure if metatable is supposed to be different
function RouterFactory ()
return setmetatable ({},mt_for_router)
end
Modem ={};
mt_for_modem = {__index=Modem}
--Modem inherits from ElectronicDevice
Modem = setmetatable({},{__index=ElectronicDevice});
--Constructor for subclass, not sure if metatable is supposed to be different
function ModemFactory ()
return setmetatable ({},mt_for_modem)
end
function Modem:WarDialNeighborhood(areaCode)
cisco = RouterFactory();
--polymorphism
cisco.Reboot(); --Call reboot on a router
self.Reboot(); --Call reboot on a modem
if (self.isOn) then self:StartDialing(areaCode) end;
end
function Modem:StartDialing(areaCode)
print('now dialing all numbers in ' .. areaCode);
end
testDevice = ElectronicDeviceFactory();
print("The device is on? " .. (testDevice:getIsOn() and "yes" or "no") );
testDevice:Reboot(); --Ok
testRouter = RouterFactory();
testRouter:ResetHardware(); -- nil value
testModem = ModemFactory();
testModem:StartDialing('123'); -- nil value
Here's an example literal transcription of your code, with a helpful Class library that could be moved to another file.
This is by no means a canonical implementation of Class; feel free to define your object model however you like.
Class = {}
function Class:new(super)
local class, metatable, properties = {}, {}, {}
class.metatable = metatable
class.properties = properties
function metatable:__index(key)
local prop = properties[key]
if prop then
return prop.get(self)
elseif class[key] ~= nil then
return class[key]
elseif super then
return super.metatable.__index(self, key)
else
return nil
end
end
function metatable:__newindex(key, value)
local prop = properties[key]
if prop then
return prop.set(self, value)
elseif super then
return super.metatable.__newindex(self, key, value)
else
rawset(self, key, value)
end
end
function class:new(...)
local obj = setmetatable({}, self.metatable)
if obj.__new then
obj:__new(...)
end
return obj
end
return class
end
ElectronicDevice = Class:new()
function ElectronicDevice:__new()
self.isOn = false
end
ElectronicDevice.properties.isOn = {}
function ElectronicDevice.properties.isOn:get()
return self._isOn
end
function ElectronicDevice.properties.isOn:set(value)
self._isOn = value
end
function ElectronicDevice:Reboot()
self._isOn = false
self:ResetHardware()
self._isOn = true
end
Router = Class:new(ElectronicDevice)
Modem = Class:new(ElectronicDevice)
function Modem:WarDialNeighborhood(areaCode)
local cisco = Router:new()
cisco:Reboot()
self:Reboot()
if self._isOn then
self:StartDialing(areaCode)
end
end
If you were to stick to get/set methods for properties, you wouldn't need __index and __newindex functions, and could just have an __index table. In that case, the easiest way to simulate inheritance is something like this:
BaseClass = {}
BaseClass.index = {}
BaseClass.metatable = {__index = BaseClass.index}
DerivedClass = {}
DerivedClass.index = setmetatable({}, {__index = BaseClass.index})
DerivedClass.metatable = {__index = DerivedClass.index}
In other words, the derived class's __index table "inherits" the base class's __index table. This works because Lua, when delegating to an __index table, effectively repeats the lookup on it, so the __index table's metamethods are invoked.
Also, be wary about calling obj.Method(...) vs obj:Method(...). obj:Method(...) is syntactic sugar for obj.Method(obj, ...), and mixing up the two calls can produce unusual errors.
There are a number of ways you can do it but this is how I do (updated with a shot at inheritance):
function newRGB(r, g, b)
local rgb={
red = r;
green = g;
blue = b;
setRed = function(self, r)
self.red = r;
end;
setGreen = function(self, g)
self.green= g;
end;
setBlue = function(self, b)
self.blue= b;
end;
show = function(self)
print("red=",self.red," blue=",self.blue," green=",self.green);
end;
}
return rgb;
end
purple = newRGB(128, 0, 128);
purple:show();
purple:setRed(180);
purple:show();
---// Does this count as inheritance?
function newNamedRGB(name, r, g, b)
local nrgb = newRGB(r, g, b);
nrgb.__index = nrgb; ---// who is self?
nrgb.setName = function(self, n)
self.name = n;
end;
nrgb.show = function(self)
print(name,": red=",self.red," blue=",self.blue," green=",self.green);
end;
return nrgb;
end
orange = newNamedRGB("orange", 180, 180, 0);
orange:show();
orange:setGreen(128);
orange:show();
I don't implement private, protected, etc. although it is possible.
If you don't want to reinvent the wheel, there is a nice Lua library implementing several object models. It's called LOOP.
The way I liked to do it was by implementing a clone() function.
Note that this is for Lua 5.0. I think 5.1 has more built-in object oriented constructions.
clone = function(object, ...)
local ret = {}
-- clone base class
if type(object)=="table" then
for k,v in pairs(object) do
if type(v) == "table" then
v = clone(v)
end
-- don't clone functions, just inherit them
if type(v) ~= "function" then
-- mix in other objects.
ret[k] = v
end
end
end
-- set metatable to object
setmetatable(ret, { __index = object })
-- mix in tables
for _,class in ipairs(arg) do
for k,v in pairs(class) do
if type(v) == "table" then
v = clone(v)
end
-- mix in v.
ret[k] = v
end
end
return ret
end
You then define a class as a table:
Thing = {
a = 1,
b = 2,
foo = function(self, x)
print("total = ", self.a + self.b + x)
end
}
To instantiate it or to derive from it, you use clone() and you can override things by passing them in another table (or tables) as mix-ins
myThing = clone(Thing, { a = 5, b = 10 })
To call, you use the syntax :
myThing:foo(100);
That will print:
total = 115
To derive a sub-class, you basically define another prototype object:
BigThing = clone(Thing, {
-- and override stuff.
foo = function(self, x)
print("hello");
end
}
This method is REALLY simple, possibly too simple, but it worked well for my project.
It's really easy to do class-like OOP in Lua; just put all the 'methods' in the __index field of a metatable:
local myClassMethods = {}
local my_mt = {__index=myClassMethods}
function myClassMethods:func1 (x, y)
-- Do anything
self.x = x + y
self.y = y - x
end
............
function myClass ()
return setmetatable ({x=0,y=0}, my_mt)
Personally, I've never needed inheritance, so the above is enough for me. If it's not enough, you can set a metatable for the methods table:
local mySubClassMethods = setmetatable ({}, {__index=myClassMethods})
local my_mt = {__index=mySubClassMethods}
function mySubClassMethods:func2 (....)
-- Whatever
end
function mySubClass ()
return setmetatable ({....}, my_mt)
update:
There's an error in your updated code:
Router = {};
mt_for_router = {__index=Router}
--Router inherits from ElectronicDevice
Router = setmetatable({},{__index=ElectronicDevice});
Note that you initialize Router, and build mt_for_router from this; but then you reassign Router to a new table, while mt_for_router still points to the original Router.
Replace the Router={} with the Router = setmetatable({},{__index=ElectronicDevice}) (before the mt_for_router initialization).
Your updated code is wordy, but should work. Except, you have a typo that is breaking one of the metatables:
--Modem inherits from ElectronicDevice
Modem = setmetatable({},{__index,ElectronicDevice});
should read
--Modem inherits from ElectronicDevice
Modem = setmetatable({},{__index=ElectronicDevice});
The existing fragment made the Modem metatable be an array where the first element was almost certainly nil (the usual value of _G.__index unless you are using strict.lua or something similar) and the second element is ElectronicDevice.
The Lua Wiki description will make sense after you've grokked metatables a bit more. One thing that helps is to build a little infrastructure to make the usual patterns easier to get right.
I'd also recommend reading the chapter on OOP in PiL. You will want to re-read the chapters on tables and metatables too. Also, I've linked to the online copy of the 1st edition, but owning a copy of the 2nd is highly recommended. There is also a couple of articles in the Lua Gems book that relate. It, too, is recommended.
Another simple approach for subclass
local super = require("your base class")
local newclass = setmetatable( {}, {__index = super } )
local newclass_mt = { __index = newclass }
function newclass.new(...) -- constructor
local self = super.new(...)
return setmetatable( self, newclass_mt )
end
You still can use the functions from superclass even if overwritten
function newclass:dostuff(...)
super.dostuff(self,...)
-- more code here --
end
don't forget to use ONE dot when pass the self to the superclass function
Related
Lua: how do I return the module itself when requiring a module?
I want to create a module something like this myclass.lua local myclass = { print = function() ` print(myclass.name) end, setname = function(name) myclass.name = name end } -- Constructor local function new(name) local o = {name = ""} setmetatable(o, myclass) return o end return _this_module__ <<== How can I do this?: In order to use this module like this: myclasscode = require("myclass") local object1 = myclasscode.new("hello") local object2 = myclasscode.new("goodbye")
You need to define it on your own. The "module" here, or rather the thing that require returns is whatever is returned by the required file. For instance: one.lua: return 1 and then: print(require("one")) --> 1 In the usual case you want to provide a set of functions to the user. Using tables is the most straight-forward choice to do it: myclass.lua: local function new(name) -- constructor code here end return { new=new, -- anything else that the module should provide } require will return the table with the 'new' member that holds the constructor function. User then can use it like this: local myclass = require("myclass") local instance = myclass.new("hey") Because it's just a regular value you can do whatever you want with it. You can get quite close to what you wanted with: local _this_module_ = {} function _this_module_.new(name) -- constructor code here end return _this_module_
How to use timer.performWithDelay with a method call
I'm using a Lua class to create two objects, each of which must check where the other is to determine their movement. I'm attempting to use timer.performWithDelay to have them check every second, but for some reason when I try to do this, the line o.moveCheckTimer = timer.performWithDelay(1000, o:moveCheck, 0) in the class constructor throws an error stating that "Function arguments are expected near ','". I have attempted to use an anonymous function like this: o.moveCheckTimer = timer.performWithDelay(1000, function() o:moveCheck() end, 0) but that causes the timer of both objects to only call the function for the most recent object that was created and not for itself (also very confusing behavior, and if anyone knows why this happens I would love to learn why). I have dug through the API and info on method calls thoroughly, but I can't seem to find anything that uses them both together, and I feel like I'm missing something. How can I use the method call as the listener for this timer? Here is the full constructor: Melee = {} Melee.__index = Melee function Melee:new(id, name, lane, side) local o = {} setmetatable(o, Melee) o.id = id o.name = name o.lane = lane o.side = side if name == "spearman" then o.maxhp = 100 o.range = 1 o.damage = {10, 20} o.imageName = "images/rebels/spearman.png" else error("Attempted to create melee unit with undefined name") end o.hp = o.maxhp --Display self o.image = display.newImageRect(mainGroup, "images/rebels/spearman.png", 80, 80) o.image.x = 0 o.image.y = lanes[lane] o.image.anchorY = 1 if side == 2 then o.image.xScale = -1 o.image:setFillColor(0.8) o.image.x = display.contentWidth - 100 end --Move and attack local destination = display.contentWidth if side == 2 then destination = 0 end o.moving = 1 o.movement = transition.to(o.image, {x = destination, time = 30000+math.random(-200,200)}) o.moveCheckTimer = timer.performWithDelay(1000, o:moveCheck, 0) --o.attackTimer = timer.performWithDelay(1000, self:attack, 0) return o end
Lua class methods
I'm trying to create Ball class and have some methods in the class but I can't find the right syntax Tried reading this: https://www.lua.org/pil/16.html MovingObj = {} function MovingObj:new(o) return o end ball = MovingObj:new {} MovingObj.test = function (self) print ("Test!") end ball:test() Error message I get: attempt to call method 'test' (a nil value)
o is just a empty table, you dont apply a metatable to it which would allow access to the functions of MovingObj You can correct this by applying a metatable during your new function: MovingObj = {} function MovingObj.new(o) o = o or {} local meta = { __index = MovingObj -- when o does not have a given index check MovingObj for that index. } return setmetatable(o, meta) -- return o with the new metatable applied. end ball = MovingObj.new({type = "ball"}) function MovingObj:test() print ("Test! I'm a " .. self.type) end ball:test() It is also not necessary to use the : syntax for this new function, we are not using the self variable.
Lua classes: how do I access the object when running one of it's methods?
I am using this for classes: http://pastebin.com/DHVXsH37 Here is my code: require 'class' -- General node class and functions Node = class(function(node, ID, x, y) node.nodetype = 'neutral'; node.id = ID; node.x = x; node.y = y; node.fname = 'exnode.png' end) function Node:display() print( self.fname ) local img = display.newImage( self.fname, self.x, self.y ); img:addEventListener( "tap", self.onTap ) end When running the display function of an initialized instance. I get an error saying that self is not defined. How do I get the properties of the object?
As Etan said in the comment, make sure you call your method as object:method() and not as object.method() as in the first case self value is set to object and in the second case it's not set to anything (so has nil value). object:method(...) notation is a syntax sugar for object.method(object, ...).
Matlab: How to survey compiled m-code progression from external API?
My question is extremely specific to the arcanes of the matlab compiler and runtime. As only people familiar with matlab runtime API may answer, I shortened much details. Please let me know if I should be more verbose. Introduction Using the matlab compiler & runtime I can call a function written in m-code from a C# program. Let's say calling: function [result] = foo(n) %[ result = 0; for k = 1:n, pause(1.0); % simulate long processing result = result + 42; end %] with (somewhere behind some dllimports in the C# code): mclFeval(IntPtr inst, string name, IntPtr[] plhs, IntPtr[] prhs) So far, so good, I have no issue with this (i.e intializing the runtime, loading the '.cft' file, marshalling back and forth MxArray with .Net types, etc...) My Problem I would like to survey the progression of my foo function using some cancel and progress callbacks: function [result] = foo(n, cancelCB, progressCB) %[ if (nargin < 3), progressCB = #(ratio, msg) disp(sprintf('Ratio = %f, Msg = %s', ratio, msg)); end if (nargin < 2), cancelCB = #() disp('Checking cancel...'); end result = 0; for k = 1:n, if (~isempty(cancelCB)), cancelCB(); % Up to the callback to raise some error('cancel'); end; if (~isempty(progressCB)), progressCB(k/n, sprintf('Processing (%i/%i)', k, n)); end pause(1.0); % simulate long processing result = result + 42; end %] But of course I would like these callbacks to be in the C# code, not within the m-one. Investigations Looking at 'mclmcr.h' header file, it looks like these functions may be of help: extern mxArray* mclCreateSimpleFunctionHandle(mxFunctionPtr fcn); extern bool mclRegisterExternalFunction(HMCRINSTANCE inst, const char* varname, mxFunctionPtr fcn); Unfortunatly these are fully undocumented and I found no use case I could mimic to understand how they work. I've also thought about creating a COM visible object in C# and pass it as a parameter to the matlab code: // Somewhere within C# code: var survey = new ComSurvey(); survey.SetCancelCallback = () => { if (/**/) throw new OperationCancelException(); }; survey.SetProgressCallback = (ratio, msg) => { /* do something */ }; function [result] = foo(n, survey) %[ if (nargin < 2), survey = []; end result = 0; for k = 1:n, if (~isempty(survey)), survey.CheckCancel(); % up to the COM object to raise exception survey.SetProgress(k/n, sprintf('Processing... %i/%i', k, n)); end pause(1.0); % simulate long processing result = result + 42; end %] I'm very familiar with functions to create numeric and structure arrays and know how to use them: extern mxArray *mxCreateNumericArray(...) extern mxArray *mxCreateStructArray(...) Anyhow, how COM objects are packaged to MxArrays, I don't know? Further investigations Day+1 Even if still unstable, I succeeded to have matlab to callback into my C# code and it seems that mclCreateSimpleFunctionHandle is the direction to go. Note: Below code is for reference only. It may not be suitable in your own context as is. I'll provide simpler code later on (i.e. once I'll get stable solution). Looking to the signature of the mxFunctionPtr, I created two delegates like this: // Mimic low level signature for a Matlab function pointer [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] delegate void MCRInteropDelegate(int nlhs, IntPtr[] plhs, int nrhs, IntPtr[] prhs); and // Same signature (but far more elegant from .NET perspective) delegate void MCRDelegate(MxArray[] varargouts, MxArray[] varargins); I also linked to the runtime like this: [DllImport("mclmcrrt74.dll", EntryPoint = "mclCreateSimpleFunctionHandle", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] static extern IntPtr _mclCreateSimpleFunctionHandle(MCRInteropDelegate fctn); Assuming MxArray is a .NET class of mine that simply encapsulate for mxArray* handles, I then marshaled my delegates like this: // Create MxArray from corresponding .NET delegate static MxArray CreateFromDelegate(MCRDelegate del) { // Package high level delegate signature to a 'dllimport' signature MCRInteropDelegate interopDel = (nlhs, plhs, nrhs, prhs) => { int k = 0; var varargouts = new MxArray[nlhs]; var varargins = new MxArray[nrhs]; // (nrhs, prhs) => MxArray[] varargins Array.ForEach(varargins, x => new MxArray(prhs[k++], false)); // false = is to indicate that MxArray must not be disposed on .NET side // Call delegate del(varargouts, varargins); // Todo: varargouts created by the delegate must be destroyed by matlab, not by .NET !! // MxArray[] varargouts => (nlhs, plhs) k = 0; Array.ForEach(plhs, x => varargouts[k++].getPointer()); }; // Create the 1x1 array of 'function pointer' type return new MxArray(MCRInterop.mclCreateSimpleFunctionHandle(interopDel)); } Finally, assuming module is an instance of MCRModule (again, a class of mine to encapsulate hInst* in low level mclFeval API), I was able to call foo function and have it to enter my .NET cancel delegate like this: // Create cancel callback in .NET MCRDelegate cancel = (varargouts, varargins) => { if ((varargouts != null) && (varargouts.Length != 0) { throw new ArgumentException("'cancel' callback called with too many output arguments"); } if ((varargins != null) && (varargins.Length != 0) { throw new ArgumentException("'cancel' callback called with too many input arguments"); } if (...mustCancel...) { throw new OperationCanceledException(); } } // Enter the m-code // NB: Below function automatically converts its parameters to MxArray // and then call low level mclFeval with correct 'mxArray*' handles module.Evaluate("foo", (double)10, cancel); This .NET code worked fine, and foo really made callback to the cancel delegate properly. Only problem, is that it is quite unstable. My guess is that I used too many anonymous functions, and probably some of them are disposed too early ... Will try to provide with stable solution within the next few days (hopefully with simpler code to read and copy-paste in your own context for immediate testing). Please let me know if you think I'm going the wrong direction with mclCreateSimpleFunctionHandle.
Got it mclCreateSimpleFunctionHandle was effectively the right API function to call at in order to create an array variable (on matlab's side) holding for a function pointer (on external's side). I'm now able to have compiled m-code to call back into my C# code for cancellation and progression purposes. Correct marshalling for mclCreateSimpleFunctionHandle is described here