home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #19 / NN_1992_19.iso / spool / comp / compiler / 1509 < prev    next >
Encoding:
Internet Message Format  |  1992-09-02  |  14.0 KB

  1. Path: sparky!uunet!wupost!udel!gvls1!faatcrl!iecc!compilers-sender
  2. From: matthew@ntl02.uk.tele.nokia.fi (Matthew Faupel)
  3. Newsgroups: comp.compilers
  4. Subject: Re: Dealing with Multiple Inheritence
  5. Keywords: OOP, design
  6. Message-ID: <92-09-028@comp.compilers>
  7. Date: 3 Sep 92 00:12:42 GMT
  8. References: <92-09-015@comp.compilers>
  9. Sender: compilers-sender@iecc.cambridge.ma.us
  10. Reply-To: matthew@ntl02.uk.tele.nokia.fi (Matthew Faupel)
  11. Organization: Nokia Telecommunications, Cambridge, UK
  12. Lines: 285
  13. Approved: compilers@iecc.cambridge.ma.us
  14.  
  15. Ed Cynwrig Dengler writes:
  16.  
  17. > I am trying to find references to articles that deal with the problems of
  18. > dealing with and representing multiple inheritence in a compiled program.
  19. > I am also interested in any articles that deal with how large typical
  20. > object-oriented classes get (ie. how many features/routines does a typical
  21. > class define, how many classes are typically inherited from, how deep the
  22. > inheritence tree gets, how often a feature is renamed).
  23.  
  24. > The only reference I have currently is "An Object Addressing Mechanism for
  25. > Statically Typed Languages with Multiple Inheritence" by R.C.H.  Conner,
  26. > A. Dearle, R. Morrison, and A.L. Brown.
  27.  
  28. > The reason for this request is that I am doing a project evaluating
  29. > modification of the "map" system presented in the above article, and would
  30. > like to compare the original "map" system and the modified one to any
  31. > other methods out there.
  32. ...
  33. > I am trying to find answers to the following questions:
  34. >     a) What other schemes are there?
  35. >     b) How is multiple inheritence handled (this includes what restrictions
  36. >        the scheme forces on multiple inheritence)?
  37. >     c) If an object is assigned to a variable that is of an inherited class
  38. >        type, how is the object changed to allow correct addressing of the
  39. >        fields/features/routines (eg. an object x of class D is assigned
  40. >        to variable y of class C, how is y.c handled)?
  41. >     d) Approximately how long would it take to do the assignment and/or
  42. >        find a given feature in a variable?
  43. >     e) How much space does the cheme take up?
  44.  
  45. Bjarne Stroustrup's paper on the implementation of the C++ multiple
  46. inheritance scheme can be found in:
  47.   UNIX System V, AT&T C++ Language System Release 2.0
  48.   Selected Readings.  Select Code 307-144
  49. though I don't have the original paper identification.
  50.  
  51. In addition, when I first came across Eiffel (as described in Bernard
  52. Meyer's "Object Oriented Software Construction") I was intrigued by the
  53. claim that multiple inheritance could be implemented with a fixed time
  54. being taken for looking up methods and attributes of an object.  As a
  55. result I had a go at devising schemes for implementing the Eiffel
  56. inheritance system for which this was true.
  57.  
  58. The following two schemes are what I came up with; I'm afraid I haven't
  59. read the reference that you give so I don't know whether either of them is
  60. the "map" system you mention or whether they are a duplication of already
  61. well known work, but here they are anyway.  If anyone has any comments,
  62. they'd be appreciated.
  63.  
  64. I considered using the class scheme you gave in your message as the example,
  65. however there are three details it doesn't give:
  66. o  whether the attributes are data attributes or methods,
  67. o  if the attributes are methods, which (if any) are redefined, and
  68. o  which is the main line of descent (i.e. if both B and C redefine a 
  69.    feature of A, D inherits both, and then a D is assigned to a reference
  70.    of type A, which of B or C's attribute would be used?)
  71.  
  72. So the example below (given in (possibly slightly incorrect) Eiffel) gives
  73. all of these; it also illustrates the Eiffel (2.x) trick of merging back
  74. together features that arrive by two different paths from a root class,
  75. but still under the same name; in this example, A.b and A.g are never
  76. renamed so D inherits just one version of each; A.a and A.f are renamed
  77. and so D inherits one copy from B and another from C.
  78.  
  79. class A
  80. feature
  81.    a: INTEGER;
  82.    b: INTEGER;
  83.    f: INTEGER is do Result := 1 end;
  84.    g: INTEGER is do Result := 2 end
  85. end;
  86.  
  87. class B
  88. inherit A rename a as Ba, f as Bf redefine Bf
  89. feature
  90.    Bf: INTEGER is do Result := 3 end
  91. end;
  92.  
  93. class C
  94. inherit A rename a as Ca, f as Cf redefine Cf
  95. feature
  96.    Cf: INTEGER is do Result := 4 end
  97. end;
  98.  
  99. class D
  100. inherit B, C
  101. -- D now effectively has:
  102. --   Ba: INTEGER;
  103. --   Ca: INTEGER;
  104. --   b:  INTEGER;
  105. --   Bf: INTEGER is ... end;
  106. --   Cf: INTEGER is ... end;
  107. --   g:  INTEGER is ... end
  108. -- In Eiffel 2.x there is no way of indicating which parent takes priority, 
  109. -- but we'll assume for this example that B does i.e.
  110. --   anA: A; !D!anA; anA.f;
  111. -- will end up calling Bf.
  112. end;
  113.  
  114.  
  115. Both schemes follow the same basic pattern: for each class there is a
  116. class descriptor set up by the compiler which contains pointers to the
  117. methods for that class and offsets to the data attributes from the start
  118. of any object of that class.  Objects of that class contain the data
  119. attributes of the object plus a pointer to the class descriptor.
  120.  
  121. This works fine for single inheritance however for multiple inheritance,
  122. something a bit more tricksy is required.  What the class descriptor
  123. becomes is a set of the above type of descriptor; one for each route that
  124. can be found from a common ancestor to the current class (sort of).  To
  125. give a more concrete example, if A is the descriptor for class A; B the
  126. extra information needed over and above A to describe B and so on, then
  127. the descriptors for A, B, C and D are:
  128.  
  129. A: A     B: AB     C: AC     D:ABACD
  130.                                  ^
  131. The trick is that when an object of type D is being referred to through a
  132. reference of type A or B (main line) the whole descriptor is used; when it
  133. is referred to through a descriptor of type C (or of type A after being
  134. assigned through a sequence such as: aC = aD; anA = aC) the subsidiary
  135. part of the descriptor is used.
  136.  
  137. This begs the question, how do we know to use the subsidiary part?  This
  138. is where the two solutions diverge.  In solution a) each reference to an
  139. object is made up of a pointer to the object and an offset into the class
  140. structure; the object itself contains a pointer to the class structure and
  141. this added to the offset provides the correct part to use.  In solution b)
  142. the reference is a pointer alone, but each object has as many pointers as
  143. there are subparts to the class and the reference points to the
  144. appropriate pointer in the object.
  145.  
  146. The following shows the memory setup under the two schemes when an object
  147. of type D is allocated and then referred to through two references, one of
  148. type C and the other of type D.  A class header of size 2 is assumed.
  149.  
  150. a)
  151.  
  152.     Reference          Object data        Class data         Method code
  153.  
  154. aD: +-------+          0+-------+         0+-------+
  155.     |Ptr: ^------+----->|   ^------------->|Class  |
  156.     +-------+    |     1+-------+          |Header |
  157.     |Off: 2 |    |      |Ba:  ? |         2+-------+          +-------+
  158.     +-------+    |     2+-------+          |OBa: 1 |  /------>|  Bf   |
  159.                  |      |b:   ? |          +-------+  |       | Code  |
  160. aC: +-------+    |     3+-------+          |Ob:  2 |  |       +-------+
  161.     |Ptr: ^------/      |Ca:  ? |          +-------+  |
  162.     +-------+           +-------+          |Bf:  ^----/       +-------+
  163.     |Off: 6 |                              +-------+     /--->|  g    |<-\
  164.     +-------+                              |g:   ^-------/    | Code  |  |
  165.                                           6+-------+          +-------+  |
  166.                                            |OCa: 3 |                     |
  167.                                            +-------+          +-------+  |
  168.                                            |Ob:  2 |  /------>|  Cf   |  |
  169.                                            +-------+  |       | Code  |  |
  170.                                            |Cf:  ^----/       +-------+  |
  171.                                            +-------+                     |
  172.                                            |g:   ^-----------------------/
  173.                                            +-------+
  174.  
  175. The code (in C ignoring casts) for some example actions using this object:
  176.  
  177. aD.b     -> *(aD.Ptr + *(*aD.Ptr+aD.Off+CLASS_D_B_OFF));
  178. aD.g     -> *(**aD.Ptr+aD.Off+CLASS_D_G_OFF) (aD.Ptr,aD.Off+D_TO_A_CAST);
  179. aD.Cf    -> *(**aD.Ptr+aD.Off+CLASS_D_CF_OFF) (aD.Ptr,aD.OFF+D_TO_C_CAST);
  180. aC := aD -> aC.Ptr = aD.Ptr; aC.Off = aD.Off+D_TO_C_CAST;
  181. aC = aD  -> aC.Ptr == aD.Ptr;
  182.  
  183. The parameter to the function calls is a "self" reference of the type the
  184. method is expecting passed as a pointer and offset.
  185.  
  186. The various constants used can be determined at compile time and are:
  187. #define CLASS_D_B_OFF  3  // Offset of b's offset in D's class data
  188. #define CLASS_D_G_OFF  5  // Offset of pointer to g in D's class data
  189. #define CLASS_D_CF_OFF 8  // Offset of pointer to Cf in D's class data
  190. #define D_TO_A_CAST    0  // Ref.Off change required to make a D an A
  191. #define D_TO_C_CAST    4  // Ref.Off change required to make a D a C
  192.  
  193. Advantages (compared to solution b)
  194.   Reference comparison is easier.
  195.   Assignment is easier.
  196.   New object initialisation is quicker.
  197.   Object points to class header => easy to determine type of object without
  198.   storing extra info.
  199.   Potentially less space is taken by each object, however as references
  200.   take more and many objects contain references, this advantage is probably
  201.   lost.
  202.  
  203. Disadvantages
  204.   References require 2 values => using registers is more tricky => code
  205.   is not as efficient.
  206.   Extra work is required to resolve attributes (due to adding in the
  207.   offset from the reference).
  208.   
  209.  
  210. b)
  211.  
  212.     Reference          Object data        Class data         Method code
  213.  
  214. aD: +-------+          0+-------+         0+-------+
  215.     |   ^-------------->|   ^----------\   |Class  |<-\
  216.     +-------+          1+-------+      |   |Header |  |
  217.                   /---->|   ^-------\  |  2+-------+  |
  218.                   |    2+-------+   |  \-->|H:   ^----+
  219.                   |     |Ba:  ? |   |      +-------+  |
  220. aC: +-------+     |    3+-------+   |      |O:   0 |  |
  221.     |   ^---------/     |b:   ? |   |      +-------+  |
  222.     +-------+          4+-------+   |      |OBa: 2 |  |       +-------+
  223.                         |Ca:  ? |   |      +-------+  |  /--->|  Bf   |
  224.                         +-------+   |      |Ob:  3 |  |  |    | Code  |
  225.                                     |      +-------+  |  |    +-------+
  226.                                     |      |Bf:  ^-------/
  227.                                     |      +-------+  |       +-------+
  228.                                     |      |g:   ^----------->|  g    |<-\
  229.                                     |     8+-------+  |       | Code  |  |
  230.                                     \----->|H:   ^----/       +-------+  |
  231.                                            +-------+                     |
  232.                                            |O:   1 |          +-------+  |
  233.                                            +-------+     /--->|  Cf   |  |
  234.                                            |OCa: 3 |     |    | Code  |  |
  235.                                            +-------+     |    +-------+  |
  236.                                            |Ob:  2 |     |               |
  237.                                            +-------+     |               |
  238.                                            |Cf:  ^-------/               |
  239.                                            +-------+                     |
  240.                                            |g:   ^-----------------------/
  241.                                            +-------+
  242.  
  243. The code (in C ignoring casts) for some example actions using this object:
  244.  
  245. aD.b     -> *(aD + *(*aD+CLASS_D_B_OFF));
  246. aD.g     -> *(**aD+CLASS_D_G_OFF) (aD+D_TO_A_CAST);
  247. aD.Cf    -> *(**aD+CLASS_D_CF_OFF) (aD+D_TO_C_CAST);
  248. aC := aD -> aC = (aD?aD+D_TO_C_CAST:0)
  249. aC = aD  -> aC == (aD?aD+D_TO_C_CAST:0)
  250.  
  251. The various constants used can be determined at compile time and are:
  252. #define CLASS_D_B_OFF   5  // Offset of b's offset in D's class data
  253. #define CLASS_D_G_OFF   7  // Offset of pointer to g in D's class data
  254. #define CLASS_D_CF_OFF 12  // Offset of pointer to Cf in D's class data
  255. #define D_TO_A_CAST     0  // Pointer change required to make a *D a *A
  256. #define D_TO_C_CAST     1  // Pointer change required to make a *D a *C
  257.  
  258. Advantages (compared to solution a)
  259.   Attribute resolution is easier.
  260.   A reference is a single value, which is easier to use efficiently.
  261.  
  262. Disadvantages
  263.   Pointers to the same object can have different values.
  264.   The pointer doesn't neccesarily point to the head of the object, so
  265.   extra information has to be stored in the class data to allow the
  266.   object head to be found (the O field is the offset from the head of
  267.   the object).
  268.   The pointer(s) to the class data from the object do not point to the
  269.   head of the class, hence extra information (the pointer H) is needed
  270.   to determine where the class data head is.
  271.   Object initialisation time increases as the number of routes to a class
  272.   through the inheritance tree increases (though this can be mitigated by
  273.   setting up a template object and block copying it).
  274.   
  275. General advantages of both methods
  276.   Minimum space is used to store attributes (i.e. no unnecessary duplication
  277.   of attributes when a parent is inherited twice).
  278.   Code is shared where appropriate, not duplicated.
  279.   The following inheritance features are supported:
  280.    o  Multiple inheritance (including from the same class more than once)
  281.    o  Renaming and redefining
  282.    o  Only those multiply included parent features that need to be
  283.       duplicated are duplicated
  284.    o  Replacement of a method by an attribute is possible (though I'll
  285.       leave that as an exercise for the reader)
  286.    o  The ability to define the main line of descent when inheriting from
  287.       the same class more than once (I believe Eiffel 3 supports this on a
  288.       per attribute basis; I don't think the above schemes can handle that,
  289.       though I can think of a tacky modification to make them do so :-)
  290.  
  291. Apologies for the long post; hope someone finds this interesting!
  292.  
  293. Matthew
  294. --
  295. matthew@uk.tele.nokia.fi *---
  296. matthew@ntl02.decnet.nokia.fi *------------------* Matthew Faupel *---
  297. -- 
  298. Send compilers articles to compilers@iecc.cambridge.ma.us or
  299. {ima | spdcc | world}!iecc!compilers.  Meta-mail to compilers-request.
  300.