-
Notifications
You must be signed in to change notification settings - Fork 20
/
oo.lua
259 lines (218 loc) · 8.83 KB
/
oo.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
--[[
Title: Object oriented programming
Author(s): LiXizhi
Date: 2009/10/31
Desc: OO programming in lua.
It provides class inheritance and C++ like class constructor mechanism.
Alternatively, one can use the class in NPL.load("(gl)script/ide/ObjectOriented/class.lua");
which has better method performance and manual constructor implementation.
note1: inside ctor function, parent class virtual functions are not available,since meta table of parent is not set yet.
note2: In a class, all functions are virtual functions except the constructor self:ctor()
Use Lib:
-------------------------------------------------------
NPL.load("(gl)script/ide/commonlib.lua");
-- define a base class with constructor
local myBaseClass = commonlib.inherit(function(o)
o.myTable = o.myTable or {name="myBaseClass"}
-- note: inside ctor function, parent class virtual functions(such as PrintMe) are not available,since meta table of parent is not set yet.
commonlib.log("in ctor of base: %s\n", o:PrintMe())
end)
function myBaseClass:PrintMe()
log("base: PrintMe->")
return commonlib.serialize(self.myTable);
end
local myDerivedClassA = commonlib.inherit(myBaseClass, {
static_non_table_field = "default initializer",
}, function(o)
o.myTable.nameA = "A";
commonlib.log("in ctor of A: %s\n", o:PrintMe())
end)
function myDerivedClassA:PrintMe()
log("A: PrintMe->")
return commonlib.serialize(self.myTable);
end
local myDerivedClassB = commonlib.inherit(myDerivedClassA, {
static_non_table_field_B = "default initializer",
})
-- we can alternatively define constructor function as ctor() at a later time.
function myDerivedClassB:ctor()
self.myTable.nameB = "B";
commonlib.log("in ctor of B: %s\n", self:PrintMe())
end
function myDerivedClassB:PrintMe()
log("B: PrintMe->")
return commonlib.serialize(self.myTable);
end
local myB = myDerivedClassB:new();
myB.myTable.nameA = "A modified by B";
commonlib.echo(myB:PrintMe())
commonlib.echo(myDerivedClassA:new():PrintMe())
commonlib.echo(myDerivedClassB:new():PrintMe())
-------------------------------------------------------
]]
if(not commonlib) then commonlib={}; end
local type = type;
-- 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
-- multiple inheritance
-- see. http://www.lua.org/pil/16.3.html
function commonlib.multi_inherit(...)
local arg = {...};
arg.n = select("#", ...);
local c = {} -- new class
-- class will search for each method in the list of its
-- parents (`arg' is the list of parents)
setmetatable(c, {__index = function (t, k)
-- With this trick, accesses to inherited methods are as fast as to local methods (except for the first access).
-- The drawback is that it is difficult to change method definitions after the system is running, because these changes do not propagate down the hierarchy chain
local v = search(k, arg)
t[k] = v -- save for next access
return v
end})
-- prepare `c' to be the metatable of its instances
c.__index = c
-- define a new constructor for this new class
function c:new (o)
o = o or {}
setmetatable(o, c)
return o
end
-- return new class
return c
end
-- create a new class inheriting from a baseClass.
-- the new class has new(), _super, isa() function.
-- @param baseClass: the base class from which to inherit the new one. it can be nil if there is no base class.
-- if it is string, baseClass = commonlib.gettable(baseClass) is used to obtain the class
-- @param new_class: nil or a raw table.
-- if it is string, new_class = commonlib.gettable(new_class) is used to obtain the class
-- @param ctor: nil or the constructor function(o) end, one may init dynamic table fields in it. One can also define new_class:ctor() at a later time.
-- note: inside ctor function, parent class virtual functions are not available,since meta table of parent is not set yet.
-- @return the new class is created. One can later create and instance of the new class by calling its new function().
function commonlib.inherit(baseClass, new_class, ctor)
if(type(baseClass) == "string") then
baseClass = commonlib.gettable(baseClass);
end
if(type(new_class) == "string") then
new_class = commonlib.gettable(new_class);
end
if(not ctor and type(baseClass) == "function") then
ctor = baseClass;
baseClass = nil;
end
local new_class = new_class or {}
local class_mt = { __index = new_class }
-- this ensures that the base class new function is also called.
function new_class:new(o)
local o = o or {}
if(baseClass) then
-- this ensures that the constructor of all base classes are called.
if(baseClass.new~=nil) then
baseClass:new(o);
end
end
setmetatable( o, class_mt )
-- please note inside ctor function, parent class virtual functions are not available,since meta table of parent is not set yet.
local ctor = rawget(new_class, "ctor");
if(type(ctor) == "function") then
ctor(o);
end
return o
end
new_class.ctor = ctor
if (baseClass~=nil) then
setmetatable( new_class, { __index = baseClass } )
end
--------------------------------
-- Implementation of additional OO properties
--------------------------------
-- Return the class object of the instance
function new_class:class()
return new_class
end
-- Return the super class object of the instance
new_class._super = baseClass
--[[ Xizhi: having the Java like super() method is inefficient in lua.
-- the following commented code only provide read access to method.
-- Instead one should use class_name._super.Method(self, ...) instead.
-- Recursivly allocates the inheritance tree of the instance.
-- @param mastertable The 'root' of the inheritance tree.
-- @return Returns the instance with the allocated inheritance tree.
function new_class.alloc_(mastertable)
local instance = {}
-- Any functions this instance does not know of will 'look up' to the superclass definition.
setmetatable(instance, {__index = new_class, __newindex = mastertable})
return instance;
end
-- only create the super object on demand, since it consumes one more table.
-- @note: THE USE OF SUPER ONLY GRANTS ACCESS TO METHODS, NOT TO DATA MEMBERS
-- however it can write to data members.
-- added by Xizhi: 2013.12.31
function new_class:super()
local super_instance = self._super_instance;
if(super_instance) then
return super_instance;
else
if(baseClass) then
super_instance = baseClass.alloc_(self);
self._super_instance = super_instance;
return super_instance;
end
end
end
]]
-- Return true if the caller is an instance of theClass
function new_class:isa( theClass )
local b_isa = false
local cur_class = new_class
while ( nil ~= cur_class ) and ( false == b_isa ) do
if cur_class == theClass then
b_isa = true
else
cur_class = cur_class._super
end
end
return b_isa
end
return new_class
end
-- this function can be called multiple times for the same target_class with different interface_class
-- It just copies all string_key, value pair from interface_class to target_class except for ctor and those that already exist in target_class.
-- This is faster than multiple inheritance or single inheritance because the target_class contains all interface functions on its own meta table.
-- @note: interface_class's ctor function is NOT called in target_class's ctor, one has to do it manually if required.
-- @param target_class: new class to which the interface functions are copied to.
-- @param interface_class: base interface class table. please note that this table must be fully loaded when this function is called.
function commonlib.add_interface(target_class, interface_class)
for key, value in pairs(interface_class) do
if(type(key) == "string" and key ~= "ctor") then
if(target_class[key] == nil) then
target_class[key] = value;
end
end
end
end
-- lua 5.1 does not support __gc for table object. it only supports __gc for userdata.
-- this function can be called to enable __gc function to be called when object is garbage collected
-- when target_class is garbage collected. see also `commonlib.setmetatable__gc`
-- @param target_class: the table instance to which we will track garbage collection.
-- @param gc_function: if nil, target_class.__gc is used. otherwise this function is used.
function commonlib.enable__gc(target_class, gc_function)
if (_VERSION == "Lua 5.1") then
local proxy = newproxy(true);
target_class._gcproxy = proxy;
getmetatable(proxy).__gc = function()
if(gc_function) then
gc_function(target_class);
elseif(target_class.__gc) then
target_class:__gc();
end
end
else
-- lua 5.2 naturally support __gc for tables.
end
end