home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / comp / lang / cplus / 20048 < prev    next >
Encoding:
Text File  |  1993-01-28  |  20.1 KB  |  557 lines

  1. Newsgroups: comp.lang.c++
  2. Path: sparky!uunet!ukma!darwin.sura.net!sgiblab!munnari.oz.au!metro!extro.ucc.su.OZ.AU!maxtal
  3. From: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
  4. Subject: Mixins (long)
  5. Message-ID: <1993Jan28.192352.26804@ucc.su.OZ.AU>
  6. Summary: Description of mixin technology
  7. Keywords: Mixins
  8. Sender: news@ucc.su.OZ.AU
  9. Nntp-Posting-Host: extro.ucc.su.oz.au
  10. Organization: MAXTAL P/L C/- University Computing Centre, Sydney
  11. Date: Thu, 28 Jan 1993 19:23:52 GMT
  12. Lines: 543
  13.  
  14.     MIXIN SOFTWARE TECHNOLOGY
  15.     =========================
  16.  
  17. Here is a long promised description of mixins. Sorry,
  18. there are no real examples here. Its up to your imagination
  19. until I get something real prepared. But this description
  20. couldn't wait.
  21.  
  22. I hope it helps.
  23.  
  24.     John MAX Skaller, Maxtal P/L, Sydney, Australia.
  25.     maxtal@extro.ucc.su.oz.au
  26.  
  27. QUESTION: What are mixins?
  28.  
  29. ANSWER:  "Mixin" in C++ is a high power technique for using
  30. multiple inheritance (MI) with abstract virtual bases (AVB) 
  31. to enable incremental development of both interfaces and implementations
  32. of classes.
  33.  
  34. To understand mixins we first need to know the basic enabling
  35. technology. I've often commented mixins were Bjarnes greatest
  36. achievement in C++: the following two questions outline 
  37. that achievement. Note Bjarne claims it wasn't intended
  38. to enable mixin technology: so much the greater is a
  39. scientific law that has unexpected and useful consequences.
  40.  
  41.  
  42.  
  43. QUESTION: How do mixins work?
  44.  
  45. ANSWER: Mixins rely on the "sibling call" mechanism described in
  46. the ARM on p234:
  47.  
  48.     "A call to a virtual function through one path
  49.     in an inheritance structure may result in the invocation 
  50.     of a function redefined on another path. 
  51.     This is an elegant way for a base class to 
  52.     act as a means of communication between sibling classes."
  53.  
  54.                    B f()=0
  55.                   /\
  56.                  /  \
  57.         f();    X    Y g(){ f(); }
  58.                  \  /
  59.                   \/
  60.                   D
  61.  
  62. The virtual base B in this diagram contains a 'hook' or 'vector'
  63. function 'f'. The definition of 'f' is supplied in X, while
  64. 'f' is used by 'g' in Y. The class D is derived from X and Y
  65. to 'connect' the definition of 'f' in X with the usage of
  66. 'f' in Y via their shared virtual base B.
  67.  
  68. QUESTION: Why does Sibling Call work?
  69.  
  70. ANSWER: Sibling call works because of the Name Dominance Rule,
  71. described in the ARM in 10.1.1 on pp204-205:
  72.  
  73.     "A name X::f dominates a name B::f if its class X has
  74.     B as a base. If a name dominates another no ambiguity
  75.     exists between the two; the dominant name is used
  76.     when there is a choice ...
  77.  
  78.     "The dominance rule is necessary for virtual functions
  79.     since it *is* the rule for which function should be 
  80.     invoked for a virtual call ... for virtual functions
  81.     the dominance rule is what guarrantees that the
  82.     same function is called independently of the static
  83.     type of the pointer, reference, or name of the object
  84.     for which it is called"
  85.  
  86.  
  87.     Axiom Classes
  88.     -------------
  89.  
  90. The base classes in mixin technology are usually small, independent,
  91. and abstract: ideal for placement in a library.  The member
  92. functions of these classes are usually small in number,
  93. independent, and pure virtual, and may be called axioms.
  94.  
  95.     class Ax1    class Ax2    class Ax3
  96.  
  97. A set of global functions operating on the base abstractions
  98. can be written and also placed in the library:
  99. they will work for all implementations of the abstractions
  100. because of polymorphism. Note that these functions
  101. are like 'theorems' in that they are defined in terms
  102. of the undefined pure virtual axioms. 
  103.  
  104.  
  105.     g(Ax1&);    g(Ax2&);    g(Ax3&);
  106.     h(Ax1*);    h(Ax2*);    h(Ax3*);
  107.  
  108.     k(Ax1*,Ax2*);
  109.  
  110. Although global, these functions do NOT pollute the global namespace,
  111. since they are qualified by the classes they operate on:
  112. they are as attached to the class as its members, but have
  113. no access to implementation details.
  114.  
  115. The axiom classes can be combined using public virtual inheritance
  116. to form larger, convenient, sets of axioms.
  117.  
  118.     Theorem (Interface) Classes
  119.     ---------------------------
  120.  
  121. It is common practice to put a set of related 'theorem'
  122. functions in a 'Theorem' class derived from the axiom classes
  123. to which it refers. If more than one class is refered
  124. to, MI must be used. The inheritance is usually public,
  125. although sometimes it is protected or private if the
  126. axioms are considered too primitive for end-user invokation.
  127. The inheritance will almost always be virtual, so that
  128. subsequent combination of theorem classes by MI will
  129. always only ever have one instance of each axiom class.
  130.  
  131. These classes are sometimes called 'Interface' classes,
  132. since they contain higher level functionality than the
  133. primitive axiom classes, which is more suitable for
  134. use by end-users.
  135.  
  136. In general, various schemes of building on the base abstractions
  137. can be used, or no scheme at all. It is not incorrect to
  138. introduce new axioms into theorem classes, for example.
  139.  
  140. Note that in advanced mixin use, interface classes may themselves
  141. be introduced initially as abstract classes. Their pure
  142. virtuals are then defined in terms of some set of axioms
  143. by creating an implementation class which derives from both
  144. the interface class and the axiom classes, and implements
  145. the interface functions in terms of the axiom functions.
  146. The resultant interface is still abstract: but the abstraction
  147. has been shifted from the high level interface functions
  148. to the low level axiom primitives. Subsequent definition
  149. of the primitives 'concretises' the interface class.
  150.  
  151.  
  152.     Model (Implementation) Classes
  153.     ------------------------------
  154.  
  155. For each base abstraction, a typical library will contain several
  156. implementations with different properties. An implementation
  157. of an abstract class is derived *virtually* from the abstraction,
  158. and implements it by overriding the virtual functions.
  159.  
  160. Typically, one implementation will be optimised for speed, and another
  161. to use the least memory. However, the implementations
  162. may also differ in their semantics, for example the pure
  163. virtual function 'Draw()' may be overriden in different
  164. classes to draw different shapes.
  165.  
  166. The implementation of an axiom class is called a Model or
  167. Implementation class. As above, in a non-strict scheme,
  168. Model classes may implement only some of the pure virtual
  169. functions, and subsequent derivations may be needed to
  170. completely concretise the abstraction. Such derivations
  171. may also inherit other abstractions in order to concretise
  172. the first set, transfering the burden of definition
  173. sideways.
  174.  
  175.     Constructor (Mixin) Classes
  176.     ---------------------------
  177.  
  178. Now when you want to create an object which has a certain
  179. interface, you select, or build using MI, an appropriate
  180. interface class for your application. This class must
  181. be globally known, and is the class with which
  182. you perform all work.
  183.  
  184. Luckily, you dont have to write many procedures that operate
  185. on this class: they are already in the library, although
  186. they are typically defined to work on the bases: you can
  187. still call them because your interface class 'isA' base,
  188. for each of its bases.
  189.  
  190. However, the interface class is abstract, you cant
  191. make an object of the interface class directly.
  192. Instead you must 'mixin' <finally!> an implementation
  193. of each axiom base by multiple virtual inheritance.
  194.  
  195. Suppose you have an interface class IF which has axiom
  196. classes Ax1, Ax2 and Ax3. You choose to use the
  197. implementations Ax1Im3, Ax2Im7 and Ax3Im2, because these
  198. are optimised for speed and support the semantics you desire.
  199. Here's how you do the mixin:
  200.  
  201.   IF * makeIF()
  202.   {
  203.     class Dummy : public IF,
  204.         public virtual Ax1Im3,
  205.         public virtual Ax2Im7,
  206.         public virtual Ax3Im2
  207.     {};
  208.     return new Dummy;
  209.   }
  210.  
  211.  
  212. QUESTION: Where's the CODE??
  213.  
  214. ANSWER: You already wrote it. Mixins embody the idea 
  215. of reusability to the fullest possible extent. 
  216. Mixins are for lazy programmers --- the best kind :-)
  217.  
  218. You may have to write a constructor, though, because
  219. constructors are not inherited. :-(
  220.  
  221. Thats why using mixins is sometimes called 'Programming
  222. by Constructor': the constructor is the only code
  223. you often have to write.
  224.  
  225. QUESTION: Why is the mixin class declared in function local scope?
  226.  
  227. ANSWER: It would be even better to use an anonymous class: 
  228. the name of this class is not important and should not be known
  229. by the rest of the system. It is, after all, a class
  230. created purely for the purposes of implementing the interface:
  231. and it is good practice to hide implementation details.
  232.  
  233. It is often crucial that these details are hidden, and cannot
  234. be retrieved *even* with nasty hacks like dynamic downcasting.
  235. This is because the implementations that have been hooked
  236. in are too primitive for client access, and their direct
  237. use by the end user may destroy the integrity of the object.
  238.  
  239. Making the constructor class local or anonymous defeats
  240. downcasting, an excellent idea: the object should only
  241. be accessed through its declared interface.
  242.  
  243. Perhaps unfortunately it is sometimes necessary to use
  244. public derivation of the implementations so that
  245. their constructors and destructors are accessible.
  246. It is also usually necessary to name the concrete class
  247. since you cannot define a constructor for the class
  248. otherwise.
  249.  
  250. If the constructor class cannot be hidden by making it
  251. local, its a good idea to hide it by declaring it
  252. in file scope only.
  253.  
  254. Such definitions of the constructor class are
  255. called 'On-The-Fly', because you make up the class
  256. on the spot, just before you create an instance of it,
  257. purely for the purposes of creating that instance,
  258. and then forget the class immediately after the object
  259. has been constructed.
  260.  
  261. Usually, the only new functions you can sensibly add to a mixin
  262. class are those of *the* constructor and destructor,
  263. and any helpers need by those functions. Only one constructor
  264. is needed, since you only construct one object immediately
  265. after mixin defintion: you might as well use the default
  266. constructor.
  267.  
  268. It is also possible that you have selected or built an interface
  269. class that is still partially abstract. In this case
  270. you must also override the remaining pure virtual functions
  271. in the mixin class. It is also possible to override
  272. other virtual functions 'At The Last Minute' to provide
  273. more efficient access or modified functionality.
  274.  
  275. QUESTION: Is this scheme of Axiom (primitive), Theorem(Interface), 
  276. Model(Implementation) and Constructor(Mixin) classes 
  277. the only way to do mixins?
  278.  
  279. ANSWER: No. You can do anything you want :-) 
  280. The scheme outlined above is intended to indicate the 'Spirit of Mixin',
  281. and not any set of hard and fast rules.
  282.  
  283. The basic principle of mixin is simply that you should defer
  284. binding of the implementation of an interface until
  285. the last possible place, and that the basic functional
  286. primitives be encapsulated in small, independent abstract
  287. classes.
  288.  
  289. QUESTION: Is there an even more powerful technique than mixin?
  290.  
  291. ANSWER: Yes and No :-)
  292.  
  293. YES: Delegation is more powerful than mixin.
  294. With delegation the 'base' classes are accessed via
  295. a pointer (or reference) to another object, and the
  296. construction and destruction of these 'base' objects
  297. are under programmer control. 
  298.  
  299. NO: With mixin, there are also (in many implementations) 
  300. hidden pointers to virtual base sub-objects in the classes: 
  301. these pointers are needed to locate the virtual bases.
  302. However these hidden pointers are initialised and accessed
  303. correctly at all times because they are manipulated directly
  304. only by the compiler, not the programmer.
  305.  
  306. Mixin is the most powerful statically secure method available
  307. in C++. 
  308.  
  309. QUESTION: Can you combine mixins and delegation?
  310.  
  311. ANSWER: It is not at all uncommon to combine the two techniques.
  312. Mixins are prefered where possible because they are statically
  313. and dynamically secure, and because construction and destruction
  314. are managed automatically.
  315.  
  316. QUESTION: Is the use of virtual bases necessary in mixins?
  317.  
  318. ANSWER: Absolutely. Both the interface and implementation
  319. classes inherit from the axioms: one to build on the
  320. primitives, and the other to define them. If you
  321. didnt use virtual inheritance in both cases, the definitions
  322. would not be called by the interface functions:
  323.  
  324.  
  325.                           Ax f()=0  Axiom: MUST be shared
  326.                          /\
  327. virtual mandatory --->  /  \   <---- virtual mandatory
  328.                        /    \
  329. Implementation   f(); Im    If g(){ f(); } Interface
  330.                         \  /
  331.                          \/
  332.                           Mx Mixin
  333.  
  334.  
  335. QUESTION: Why does mixin enable incremental development?
  336.  
  337. ANSWER: Because you can provide, and use as you desire,
  338. implementations and abstractions in small pieces,
  339. independently. The binding of the implementation pieces
  340. is defered to the last minute: on the fly construction
  341. from the pieces.
  342.  
  343. This cannot be done with single inheritance (SI):
  344. with SI you get 'layered development', in which
  345. binding is done in layers.
  346.  
  347. Of course, delegation also provides incremental development,
  348. and is more powerful than mixin, so arguing that
  349. only SI and delegation is needed is pointless: mixin
  350. is the technology that provides greatest power while
  351. remaining statically secure.
  352.  
  353. QUESTION: Can I use mixins with templates?
  354.  
  355. ANSWER: Oh YES. Please do.
  356.  
  357. QUESTION: Whats the best way to build a class library?
  358.  
  359. ANSWER: With mixins of course. Delegation may also
  360. be required for more advanced techniques. However,
  361. delegation is not as well supported or secure as
  362. mixin in C++: use mixins wherever possible.
  363.  
  364. QUESTION: Whats the worst way to build a library?
  365.  
  366. ANSWER: Thats a leading question :-) The worst way
  367. is to use an 'Object Heirarchy' which has a single
  368. class 'Object' from which everything is derived by SI
  369. in increasing levels of complexity.
  370.  
  371. This inevitably leads to either a VERY fat interface for
  372. the 'Object' class in order to preserve polymorphic access via
  373. virtual functions, or use of downcasting to emulate dynamism. 
  374.  
  375. Both these methods are ugly in C++, and break all the known
  376. principles of good modular design:
  377.  
  378.     o Small interfaces
  379.     o Weak coupling
  380.     o Explicit coupling
  381.     o Hiding of implementation
  382.     o Closure of modules to changes
  383.     o Openness of modules to extension
  384.     o Reusability
  385.  
  386.     
  387. Those not believing this should try to combine the use
  388. of several Object based class libraries. The original
  389. Borland and NIH libraries are examples.
  390.  
  391. QUESTION: Why dont mixins have this problem?
  392.  
  393. ANSWER: Because a mixin library is based on a set of small,
  394. independent abstractions. Using two mixin libraries together
  395. is just a case of taking the union of the abstractions.
  396.  
  397.  
  398. QUESTION: Does use of mixins lead to an exponential explosion
  399. of classes?
  400.  
  401. ANSWER: Not necessarily, it should do precisely the opposite.
  402. The sets of basic abstractions and their implementations
  403. can be combined by a simple union: the numbers of
  404. such classes is just a simple addition --- its linear not
  405. exponential.
  406.  
  407. Certainly, the number of *possible* combinations you can
  408. have with mixins is exponential --- its supposed to be!
  409. This is the whole point: you can have a very wide choice
  410. of interfaces, yet implement them by a simple mixin
  411. of some set of implementation classes.
  412.  
  413. But these choices are made 'on the fly': the existant
  414. combinations in any one program will always be small,
  415. and the differing implementation choices are irrelevant
  416. because they're hidden.
  417.  
  418. Again, the number of useful interface classes you can
  419. form will be exponential on the axiom classes, and this
  420. is desirable. You dont have to actually create every such
  421. combination.
  422.  
  423. Mixin libraries should be considered a toolkit of reusable
  424. components, together with a few common combinations of these,
  425. supplied for convenience.
  426.  
  427. A mixin library is not a monolithic, integrated, tightly
  428. coupled system like an 'Object Heirarchy'.
  429.  
  430. QUESTION: Are mixin libraries hard to design?
  431.  
  432. ANSWER: Yes, all reusable components are hard to design.
  433. It is not easy to decide exactly how to divide up functionality
  434. between classes.
  435.  
  436. QUESTION: Are mixin libraries hard to use?
  437.  
  438. ANSWER: No, they're quite easy to use: often you need only
  439. derive an appropriate class, mixing in the desired implementation
  440. components, and possibly writing a constructor.
  441.  
  442. However, you should note that a mixin library does not
  443. present you with a total solution to your problem. It still
  444. leaves the task of designing solutions and implementing
  445. them up to you, the programmer. It presents a wide variety
  446. of solutions: you have much more choice than with an Object
  447. Library.
  448.  
  449. What the mixin library does is save you writing code: you will
  450. spend more time deciding which classes to use, and how to
  451. plug them together than coding: the code is already written.
  452.  
  453. When you do have to write code, its usually to supply a new
  454. implementation of an axiom class. In that case, the constraints
  455. are extremely tight: the interface to which you must
  456. conform is well defined, and the coding job is usually trivial.
  457.  
  458. You can, and will, of course, create new axiom classes,
  459. some more application specific than others. Mixins are 
  460. wonderful in supporting this, because you only have
  461. to provide the application specific interface and
  462. implementation details, you can reuse other components
  463. togther with your application axioms just as you
  464. can when using the library classes: by using
  465. MI to combine your classes with those in the library
  466. to provide more complete application interfaces,
  467. and by using mixins to construct them.
  468.  
  469.  
  470. QUESTION: How will mixin libraries change my coding style?
  471.  
  472. ANSWER: You wont spend much time writing code. Your effort
  473. will be directed towards design rather than hacking.
  474.  
  475. QUESTION: Are there any good mixin libraries available?
  476.  
  477. ANSWER: Not that I'm aware of. There are several reasons:
  478.  
  479.     1) Early C++ didnt have mixins: there was no
  480.       MI, only SI. People still use class libraries
  481.       designed at this time.
  482.  
  483.     2) Many existing compilers have or had until recently,
  484.       bugs in the implementation of MI, 
  485.       abstract classes and virtual functions.
  486.  
  487.       For example, Cfront version 3 incorrectly diagnosed
  488.       mixin classes as abstract, defeating the use of mixins.
  489.       Version 3.0.1 is fixed, I believe.
  490.  
  491.     3) Mixin technology is not well understood.
  492.        Thats why I've written this article.
  493.  
  494.        Mixins use some of the newest concepts in C++:
  495.        Abstract classes, Multiple Inheritance, and
  496.        Virtual bases: in combination. And templates and
  497.        exceptions will add to this technology.
  498.     
  499.     4) Many programmers come from a Smalltalk background.
  500.        So they are still thinking of OOP in terms of
  501.        dynamics and Object Hierarchies, techniques not
  502.        suited to C++, which is an essentially a strongly typed,
  503.        static, and compiled language.
  504.  
  505.        C++ OOP is so different from Smalltalk, that I have
  506.        trouble conceiving them as both OOPLs. In fact I
  507.        tend to think of C++ as a class based language,
  508.        not an object based one.
  509.  
  510.     5) There are few examples. Because of 1-4, there are few
  511.        examples for other to learn from and improve on.
  512.        I hope this will soon change :-)
  513.        Use of a technique is exponential, there is the well
  514.        known 'Snowball' or 'Band Wagon' effect.
  515.  
  516.        The situation is improving: already Borland, for example,
  517.        has replaced its Object based classes with the much
  518.        better BIDS classes. Although these are not yet
  519.        quite mixin classes (BC 3.1 has the mixin disabling bug),
  520.        they do use templates and MI: the precursor to
  521.        mixins.
  522.  
  523. QUESTION: Can I get around the mixin disabling bug?
  524.  
  525. ANSWER: Yes, you can just define a dummy body for your
  526. pure virtual functions: it will never get called, but
  527. it does make the code look messy, and it does prevent
  528. the linker detecting when you have failed to supply
  529. an implementation for a pure virtual (since they're
  530. not pure virtual if you give a dummy body).
  531.  
  532. QUESTION: Can you show me an example of mixins?
  533.  
  534. ANSWER: Yes I promise! But not yet, I'm still working on one that
  535. is non-trivial and actually usable. This is not so easy,
  536. as my compiler has the mixin disabling bug. The compromise
  537. between a 'Micky-Mouse' example (not convincing) and
  538. an industrial strength one (lots of work and may have
  539. too much detail for educational purposes) is an example
  540. of the sort of design problem that I'm grappling with.
  541.  
  542. I'm currently working on a text screen based database.
  543. (No, not a persistent object store, just a device independent
  544. record manager with windows, dialog boxes etc--usable but
  545. not sophisticated).
  546.  
  547. However, I invite those few programmers that have used
  548. mixins to post examples.
  549.  
  550.  
  551.  
  552. -- 
  553. ;----------------------------------------------------------------------
  554.         JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
  555.     Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
  556. ;------ SCIENTIFIC AND ENGINEERING SOFTWARE ---ph:  2 799 8223 --------
  557.