2 This document tries to explain the choices that
3 are made in deciding the design of the new OOP
4 system, which will be used fo (among other things)
11 - have fast method invocation. Especially when calling the same method
12 on the same objetc many times. (Eg. GetPixel()/SetPixel())
15 - allow non-centralized allocation of IDs. This means that there is no
16 agency somewhere in the world where you have to allocate an ID. IDs
17 are allocated at runtime.
20 - be location transparent. Ie. you invoke a
21 method the same way as for a local objet, even if
22 the object is owned by another task, or is
23 located on a different machine.
26 - support transparent object migration. This means that the object, the
27 system or the user can decide that the object should be moved or copied
28 to another host. Uses: If an object is accessed more often from a remote
29 host than locally, it can create a copy of itself. If the object is deleted
30 locally, it can move itself to the remote host.
36 - Thread safe method invocation.
41 Class - A collection of Interfaces and a description of the Objects
44 Object - Instance of a class. If you think of a Class as a blueprint of
45 something, then an Object is what you get when you build the something
46 after the instructuctions of the blueprint.
48 Method - A method is a function which is called with a class, an object
49 and a message as arguments.
51 Interface - An interface is a view of an object. Interfaces are
52 defined globally. A class can implement any number of Interfaces.
53 An Interface contains a table of Methods.
55 Method Stub - This is a normal C function which is called with an
56 object and a message. It is used to simplify the call of a method.
57 The Method Stub extracts the class from the Object and the Interface
58 from the Full Method ID and invokes the Method.
60 Method Base - The lowest ID of all methods on an Interface. Along with the
61 Method Count, this is used to determine if a method if supported by an
62 Interface. This is a constant during the lifetime of the system.
64 Method Count - The number of methods of an Interface. This value
65 can change (it is possible to add methods to an interface during
68 Interface ID - The ID of an Interface. It is used to look up an
69 Interface when a Method should be invoked.
70 This is a constant during the lifetime of the system.
72 Method Offset - The offset to the Method Base for a Method. Always
73 positive. This is a constant during the lifetime of the system.
75 Method ID - The addition of Method Base and Method Offset for a specific
76 Method. This is a constant during the lifetime of the system.
77 If you have an Interface Object, you can call the method.
79 Full Method ID - The addition of Method ID and the Interface ID. The
80 Interface ID is used to look up the Interface and then the Method ID is
81 used to call the method in the Interface. This is a constant during the
82 lifetime of the system.
84 Method Name - A string with the name of a method.
86 Method Object - An Object used to store a Method to allow to invoke
89 Interface Object - An object used to store an Interface
90 which allows you to invoke all methods of that interface fast.
93 Possible solutions to design goals w/pros & cons
97 For having ultimately fast method invocation we supply method objects.
99 Example of usage of method objects:
103 struct HIDDP_Gfx_SetPixel sp_msg;
105 struct TagItem mobj_tags[] =
107 {A_Method_TargetObject, (IPTR)gfxhidd },
108 {A_Method_Message, (IPTR)&sp_msg},
109 {A_Method_MethodID, HIDDM_Gfx_SetPixel},
114 /* If you don't pass a message, then memory for one could be allocated,
115 and disposed in DisposeObject(spmethod).
116 The msg's methodid will be initialized by NewObject.
117 If you want to use other msg mem, then you
118 can use the Set method on the method object.
121 spmethod = NewObject(NULL, METHODCLASS, mobj_tags);
126 for (x = 0; x < 100; x ++)
128 for (y = 0; y < 100; y ++)
133 /* CallMethod() might be a macro */
134 CallMethod(spmethod);
141 For normal method invocation there are generally two approaches:
143 a) Hashing single methods.
144 b) Hashing interfaces, where an interface
145 is an array of methods (kinda like a AmigaOS library
148 a) is a bit faster than b)
150 In order to avoid having to hash the methods, all methods of a class
151 are in a single table. This includes the methods of all parent classes
152 so these can also be called directly (no need for DoSuperMethod()
155 For this to work, we need:
157 - A method string -> ID to convert the method name to the method ID
159 - When you allocate a new class, you must allocate a table (size =
160 parentClass->numMethods + self->numMethods), copy the parent method
161 table and overwrite/add the own methods to the table
163 - The user uses a global variable in the classbase as the basic offset
164 to call methods of this class. The header file defines the offsets.
166 Interfaces work as usual but they store maybe only the real methodID
167 (one lookup more and one method object less).
171 - When a parent class changes its method table, then the child classes
172 won't notice. This could be avoided it we copy method objects.
174 - the DoMethod() stub must do an add for each method invokation but
177 Interfaces can be implemented like this:
179 - One hash table which contains tables of methods/method IDs and the
180 key is the interface ID.
182 - One hash table which contains method objects and the key is the
183 computed interface+method ID.
185 2) Noncentralized ID allocation.
186 -------------------------------
187 To allow for this, we avoid fixed integer
188 IDs (like in BOOPSI).
189 Instead we use global variables for
192 Two ways to allocate IDs:
195 Store interface IDs in global vars, and generate method IDs on the fly as
196 sum of interface ID and a offset ID for the method.
200 extern ULONG __OOPI_Root;
202 #define I_Root __OOPI_Root
203 #define MIDX_Root_New 0
204 #define MIDX_Root_Dispose 1
206 #define M_Root_New (I_Root + MIDX_Root_New)
207 #define M_Root_Dispose (I_Root + MIDX_Root_Dispose)
211 - Relatively little CPU time used for initialization.
214 - uses more CPU time for initialization.
217 Use one global var for each methodID.
221 - Leaves more flexibility for future changes.
222 - Allows to specify each method as a string
227 - Very hard to initialize
230 As for AttrIDs, there is a problem with using II), because then one can't
231 use switch() to parse AttrIDs.
233 Solution: Use methods for set & get of each attribute. which will use some
236 To avoid the problems with init, we can use a global ClassBase (like a
237 library base) and use a public field in there to store the offsets. As for
238 attributes, a class should allocate a fixed number of IDs for its
239 attributes and store the base in the ClassBase. The header file then
240 contains offsets for each Attribute. In the class, you can very quickly
241 check if an attribute is part of the class:
243 ULONG id = tag->ti_Tag - class->cl_attrBase;
245 if (id < class->cl_numAttrs)
246 ... handle own attributes
248 ... handle attributes of other classes
250 Location Transparent and Object Migration
251 -----------------------------------------
253 To allow these two, we use method objects. Method objects
254 contain the following attributes:
256 - A pointer to the method
260 - If several objects are created for the same method and the
261 method, for which they were created, changes, then all other
262 method objects change as well.
263 - The Method class contains a DoMethod() which is used to
264 call the method stored in the object.
266 By overloading DoMethod() of the Method class, you can implement
267 thread safe objects, migrate objects to other computers, etc.
273 The easy part is to create the Exception class and the objects.
277 - How do I get the position information (stack frames, function names,
278 local variables (?), line numbers, filename) ?
280 - How do I "throw" an exception ?
282 - How do I catch an exception ?
285 On Unix, I can use GNU C extensions for most things. With a special
286 assembler, it should be possible to create position information and update
289 To throw and catch an exception, I can use a global array which contains
290 jmp_bufs and catched exceptions and throw just calls a global function
291 which looks through the array for a matching catch and does a longjmp()
292 to the place. We can use macros to throw and catch exceptions.
295 On Amiga, we might be able to use similar techniques. The globals
296 must be stored in the ETask info.
298 Thread safe method invocation.
299 -----------------------------
301 This can be done through the use of proxy objects
302 and IPC, where the server runs inside one task,
303 an the clients (other tasks) get proxies to the object
304 residing inside the server.
305 The server will the Wait() for incoming
306 methods and execute them on the proxied
307 object. Currently the system is using Exec messages,
308 and since AROS has no MP (yet), I only have to pass a
312 A problem that has to be addressed here is
313 deadlocks. Say task A has object a, and task B
316 Now, A calls a method on b, but the method calls another
317 method on a. Since A will be Wait()ing for a response from
318 B, then it can't execute the method, and both
319 tasks will be waiting forever.