home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_C / CL187A.ZIP / CL187A.TXT < prev    next >
Text File  |  1994-03-15  |  202KB  |  5,311 lines

  1.  
  2.  
  3.  
  4.  
  5.                               Container Lite
  6.  
  7.                           version 1.87a, 3-15-94
  8.  
  9.                            Portable, Persistent,
  10.  
  11.                           Hybrid Container Class
  12.  
  13.                           for AT&T C++ 2.x & 3.x
  14.  
  15.  
  16.  
  17.                               Copyright 1994
  18.                                John W. Small
  19.                             All rights reserved
  20.  
  21.  
  22.  
  23.                            PSW / Power SoftWare
  24.                               P.O. Box 10072
  25.                         McLean, Virginia 22102 8072
  26.                                     USA
  27.  
  28.  
  29.                            Voice: (703) 759-3838
  30.  
  31.  
  32. Software License Agreement
  33.  
  34.  
  35.      PSW / Power SoftWare licenses the bona fide purchaser of the
  36.      Container Lite to use the tool in the development of software without
  37.      royalty.  The licensee may not publish any portion of the tool kit and
  38.      may only make backup copies of software as a means of protecting
  39.      the licensee's investment against loss or damage.
  40.  
  41.  
  42. Limited Warrantee
  43.  
  44.  
  45.      PSW / Power SoftWare warrants the Container Lite toolkit to be free
  46.      from defects in materials and workmanship for a period of 90 days
  47.      from the original purchase date and will replace same if found to be
  48.      defective and reported in writing to PSW within this period.  The
  49.      fitness of the Container Lite toolkit for any particular application is
  50.      not implied nor guaranteed.  All other liabilities which may be
  51.      construed are specifically disclaimed except where and when required
  52.      by law.
  53.  
  54.  
  55. Technical Support
  56.  
  57.  
  58.      Have a question, comment, or suggestion?  I'm looking forward to
  59.      hearing from you.  If hard copy mail is too slow you can contact me
  60.      directly as indicated below.
  61.  
  62.                John W. Small
  63.                Voice: (703) 759-3838 evenings
  64.  
  65.  
  66. Acknowledgments
  67.  
  68.  
  69.      I wish to thank my Lord and Savior, Jesus Christ, who faithfully died
  70.      for my sins taking them upon himself along with God's wrath (hell)
  71.      meant deservingly for me, so that I might be drawn, albeit
  72.      unwillingly, to receive God's unmerited gift of reconciliation and
  73.           new and eternal life.
  74.  
  75.  
  76.                                                                    Contents
  77.  
  78.  
  79. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5
  80.  
  81. Getting Started. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7
  82.  
  83. Hybrid Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
  84.      Elastic Array . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
  85.      Stack - Queue - Deque . . . . . . . . . . . . . . . . . . . . . . . 16
  86.      List. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
  87.      Sort - Search - Unique. . . . . . . . . . . . . . . . . . . . . . . 21
  88.      Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
  89.  
  90. Strong Type Checking . . . . . . . . . . . . . . . . . . . . . . . . . . 29
  91.      Additional Functionality. . . . . . . . . . . . . . . . . . . . . . 31
  92.      A Well Endowed Data Type. . . . . . . . . . . . . . . . . . . . . . 35
  93.      Deficient Data Types. . . . . . . . . . . . . . . . . . . . . . . . 40
  94.      Mediator Functions. . . . . . . . . . . . . . . . . . . . . . . . . 43
  95.  
  96. Protected Scope Virtual Functions. . . . . . . . . . . . . . . . . . . . 47
  97.  
  98. Polymorphic Nodes. . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
  99.  
  100. Reference. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
  101.      Type-less Container . . . . . . . . . . . . . . . . . . . . . . . . 69
  102.           Internals. . . . . . . . . . . . . . . . . . . . . . . . . . . 69
  103.           Constructors and Destructor. . . . . . . . . . . . . . . . . . 74
  104.           Housekeeping . . . . . . . . . . . . . . . . . . . . . . . . . 76
  105.           Elastic Array. . . . . . . . . . . . . . . . . . . . . . . . . 78
  106.           Stack, Queue, and Deque. . . . . . . . . . . . . . . . . . . . 83
  107.           List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
  108.           Sort, Search, and Unique . . . . . . . . . . . . . . . . . . . 88
  109.      FunctionRegistry. . . . . . . . . . . . . . . . . . . . . . . . . . 93
  110.      Streamable. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
  111.      ClassRegistry . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
  112.      Mutual. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .100
  113.      MutualHoldingPen. . . . . . . . . . . . . . . . . . . . . . . . . .105
  114.  
  115. Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .107
  116.  
  117.  
  118.                                                                Introduction
  119.  
  120. The Container Lite (CL v1.87a) is a portable, persistent, hybrid container
  121. class library compatible with the AT&T C++ 2.x, 3.x standard.  Full
  122. source code is provided along with "forms" for compilers not yet supporting
  123. templates.  Applications using Container Lite use only a small fraction of the
  124. container code space consumed by other commercial and compiler bundled
  125. container class libraries.  Use Container Lite repeatedly throughout all your
  126. applications for even greater savings in design and coding effort as well as
  127. drastically reducing their sizes.   Even if you were to incorporate the entire
  128. Container Lite library into your application it would add less than an austere
  129. 10K bytes to its code size!  Conventional container libraries can't even come
  130. close to a multiple of that figure or capability. Container Lite is ideal for
  131. coding games, rolling your own application frameworks, and any application
  132. requiring user configurations to persist.
  133.  
  134. Container Lite is based upon a single container class having a hybrid stack-
  135. queue-priority_queue-deque-list-array interface.  Included amongst its
  136. members are sort, search, and iterate functions.  Traditional container
  137. libraries, on the other hand, typically incorporate these structures and
  138. functionality in a myriad hierarchy.  Their "textbook" approaches often
  139. complicate the implementation of real world algorithms, especially those
  140. requiring hybrid structures.  For example, Container Lite can be readily
  141. contorted to implement polymorphic trees and graphs.  A Container Lite
  142. container can even be saved on a stream and later reloaded while multiple
  143. references to its various elements are automatically resolved, thus allowing
  144. your complex container networks to persist without a second thought. Try
  145. that with any one of those conventional container libraries and you are in for
  146. a formidable exercise in OOP extensibility.  That's because those libraries
  147. are inherently inflexible by design with their towering, convoluted
  148. hierarchies and steep learning curves.
  149.  
  150. Container Lite is implemented as an elastic array of void pointers pointing
  151. to the data you are collecting.  Unlike a conventional C++ array that is
  152. statically sized, Container Lite can expand or contract automatically to
  153. accommodate varying numbers of cells.  You have full control over the
  154. granularity and hysteresis of this elasticity.  Template invocations
  155. additionally define methods for on the fly copy initialization, assignment,
  156. and destruction of your data nodes automatically.  Container Lite's "form
  157. template" approach provides the same automatic code generation and strong
  158. type checking for compilers not yet supporting templates.
  159.  
  160.                                                                   Chapter 1
  161.  
  162.  
  163.                                                             Getting Started
  164.  
  165.  
  166. Copy the following files to the appropriate directories for the C++ compiler
  167. and operating system you are using.
  168.  
  169.  
  170.      cl.h           Container Lite header
  171.      cl.hf          Container Lite "form templates"
  172.      cl.cpp         Container Lite source
  173.  
  174.  
  175. You may need to rename the extensions of these files to suit your compiler. 
  176. Container Lite source code is AT&T C++ version 2.x, 3.x compatible.
  177.  
  178. For those of you who don't have smart linker/librarians on your system, the
  179. Container Lite source is broken down into its individual member functions
  180. and grouped together in appropriate files to facilitate the building of
  181. libraries.  These files can be found in the "cllib.src" subdirectory on the
  182. distribution diskette.  You will find a makefile in this subdirectory.  Edit the
  183. macros in the makefile to suit your environment.  A Container Lite library
  184. can be quickly built for your compiler using this makefile.
  185.  
  186. The examples found in the text are grouped together in the "clexamps.cpp"
  187. subdirectory.
  188.  
  189. Be sure to read any readme.doc files that may appear on the distribution
  190. diskette for last minute revisions.  (Please note that a readme.doc file may
  191. not appear.)
  192.  
  193.  
  194. cl.h  Macros
  195.  
  196.  
  197.  
  198. Container Lite is compatible with AT&T C++ 2.x and 3.x compilers.  To
  199. cover such a wide spectrum of capability, the cl.h header file attempts to
  200. dynamically define four macros, i.e.
  201.  
  202.  
  203.      CL_NO_TEMPLATES     CL_NO_EXCEPTIONS
  204.      CL_NO_STDARG_H      CL_CDECL
  205.  
  206.  
  207. describing your compiler's environment by sensing your compiler-specific,
  208. predefined, versioning macro.  These macros have already been properly
  209. defined in cl.h for Borland C++ and Microsoft C++ 7.0 and 8.0
  210. compilers.
  211.  
  212. For other compilers you will need to either edit cl.h as described below or
  213. globally define these macros for your project each time you "make" it.  For
  214. an old Unix compiler that doesn't support templates or exceptions and
  215. stdarg.h isn't available the command line compile command would be:
  216.  
  217.  
  218.      CC -DCL_NO_TEMPLATES -DCL_NO_EXCEPTIONS \
  219.           -DCL_NO_STDARG_H  yourapp.cpp
  220.  
  221.  
  222. assuming the -D switch is the define macro switch for your compiler. 
  223. Otherwise the cl.h header file assumes that your compiler supports templates
  224. and exception handling along with stdarg.h.
  225.  
  226. If your compiler doesn't yet support templates, be sure to define
  227. CL_NO_TEMPLATES in cl.h whenever your compiler's distinguishing
  228. predefined version macro appears.  The comments in cl.h around line 28
  229. show how this is done.
  230.  
  231. Likewise, if your compiler doesn't yet support exceptions, be sure that cl.h
  232. defines CL_NO_EXCEPTIONS around line 56.  If your compiler doesn't
  233. support the ANSI C standard header file stdarg.h, e.g. UNIX V CC, you
  234. must define CL_NO_STDARG_H in cl.h around line 113.  Without stdarg.h
  235. the cl::forEach() iterator is not available!  If however your compiler does
  236. support stdarg.h and at the same time allows for other than the normal C
  237. calling convention you may need to define CL_CDECL in cl.h around line
  238. 133.
  239.  
  240.  
  241. Exception Handling
  242.  
  243.  
  244.  
  245. Normally, Container Lite member functions catch xalloc exceptions thrown
  246. from within returning a failure indicator instead of terminating the program
  247. without requiring a set_new_handler(0) call!  This includes any xalloc
  248. exceptions thrown from within your data's default constructor or copy
  249. initializer constructor whenever they are called by Container Lite member
  250. functions.  Even if your application calls set_new_handler(0) it makes no
  251. difference, Container Lite member functions still report heap exhaustion by
  252. returning a failure indicator, i.e. 0.  If you want Container Lite member
  253. functions to pass those exceptions on to you application code for processing,
  254. i.e. catch (...), you must comment out the CL_NO_THROW_XALLOC
  255. macro definition found in cl.h around line 74.  Alternatively, you can always
  256. globally define the CL_THROW_XALLOC macro for your entire project
  257. each time you "make" it.
  258.  
  259. All other exceptions thrown from your data's overloaded operators, e.g.
  260. operator==(), operator>(), operator=(), operator<<(), operator>>(),
  261. operator delete(), etc. are passed through to your application code.  See
  262. examp101.cpp to get an idea of how you can catch these exceptions.
  263.  
  264. Suppose your data's overload assignment operator throws the exception, e.g.
  265. xmsg.  This exception is not caught by the calling Container Lite member
  266. function.  However, Container Lite member functions are written in such a
  267. way that exceptions thrown by your data's overloaded member functions
  268. won't leave the container in an unstable state.  In other words, your
  269. exception handler can recover and continue processing instead of always
  270. being forced to terminate.
  271.  
  272. Container Lite transparently traps any dynamic memory it owns that is left
  273. dangling by a thrown exception, reclaiming it as you continue working with
  274. the container or when the container is finally destructed.  To reclaim
  275. dangling memory immediately, your exception handler should call the
  276. catch_reset() member function (see examp101.cpp).
  277.  
  278. With this in mind, please note that you should code your overloaded
  279. assignment operator so that it will throw all exceptions if possible before
  280. overwriting any target data. This way Container Lite applications can
  281. gracefully recover from exceptions with the container and all its nodes left
  282. intact!  For example, if atPutAsg() throws an exception, the node being
  283. assigned to won't be corrupted.
  284.  
  285. But remember, Container Lite will only catch "xalloc" exceptions thrown
  286. from your default or copy initializer constructors.  What happens if your
  287. data's overload assignment operator throws an xalloc exception?  Your
  288. application must trap it!  Again if you haven't overwritten any target node
  289. data you can assume the container has been left intact just as it was before
  290. its "Asg" method was called.  Be careful that you don't leave dynamically
  291. allocated memory dangling that is still "owned" by your overload operator
  292. at the time the exception thrown!  If you do, your code has a potential
  293. memory leak.
  294.  
  295. In streaming operations, i.e. save() or load(), it is assumed that your
  296. compiler switches are set so that auto (local) objects are destructed whenever
  297. an exception is thrown past them.  If you elect to turn off the calling of
  298. these destructors, be aware that file handles could be left open.  In these
  299. cases, if your handler continues processing instead of terminating, it is
  300. possible to eventually run out of file handles.  Moral: if you turn off
  301. destructor calls your exception handler should terminate the application!
  302.  
  303.  
  304.                                                                   Chapter 2
  305.  
  306.  
  307.                                                            Hybrid Container
  308.  
  309.  
  310. Always think of Container Lite in terms of its multi-faceted hybrid behavior. 
  311. One moment your container is a stack, the next a priority queue or array,
  312. etcetera.  With Container Lite you are never locked into some arcane branch
  313. class of a convoluted, towering hierarchy like you would be with a
  314. conventional "textbook" container implementation.
  315.  
  316.  
  317. Elastic Array
  318.  
  319.  
  320.  
  321. Container Lite's elastic array behavior provides the foundation for the rest
  322. of its inherent behaviors.  Imagine an array in which you can insert or
  323. extract cells at any location.  Container Lite lets you specify the granularity
  324. and hysteresis of this elasticity in constructor and/or housekeeping calls with
  325. a delta parameter.  In other words, when a new cell needs to be added how
  326. many additional spare cells should be provided to optimize future expansion
  327. efforts?  And how many spare cells should be allowed to accumulate before
  328. compaction?  The following list of member functions catalogs this elastic
  329. array behavior.
  330.  
  331.  
  332.      vector()            Limit()             setLimit()
  333.      pack()              Delta()             setDelta()
  334.      Nodes()             MaxNodes()          setMaxNodes()
  335.      vacancy()           vacancyNonElastic()
  336.      atIns()             atInsNew()          atRmv()
  337.      allRmv()            atDel()             atDelAsg()
  338.      allDel()            atPut()             atPutNew()
  339.      atPutAsg()          atGet()             operator[]()
  340.      atGetAsg()          atXchg()            index()
  341.      forEach()           detect()            select()
  342.      reject()            collect()
  343.  
  344.  
  345. If you use Container Lite's template or "form template" to generate a
  346. strongly typed checked container wrapper, you can then assign and clone
  347. your data on the fly.  Methods with "Asg" and "New" suffixes assign and
  348. clone your data respectively.  Flags are available that signal a container to
  349. delete dynamic nodes upon destruction.  Templates can also provide
  350. persistence if your data has overloaded stream insertion and extraction
  351. operators along with a default constructor.  If not you still have the option
  352. of defining template mediator functions to adapt to not so well endowed data
  353. types.
  354.  
  355. Elastic array operation is really summed up by two methods, atIns() and
  356. atRmv().  AtIns() writes a pointer value to a newly inserted cell in the
  357. container's logical array at the index indicated.  The cell originally at the
  358. index and all successive cells are pushed back one index location, expanding
  359. the physical array if necessary.  AtRmv() performs the inverse function of
  360. atIns(), extracting the indexed cell returning its pointer while pulling all
  361. successive cell indices forward by one.  The physical array is shrunk
  362. automatically if excessive space has accumulated.
  363.  
  364. If you use the Container Lite template to generate a strongly typed check
  365. wrapper class for your data, you can then use the above methods with
  366. "Asg", "New", and "Del" suffixes.  These methods allow you to assign,
  367. clone, and delete your data nodes on the fly.  Persistence is also provided
  368. which we will see in our first example.
  369.  
  370.  
  371.      // examp201.cpp -- link with cl.obj
  372.  
  373.      #include <stdlib.h> // rand(), srand()
  374.      #include <time.h>   // time_t, time()
  375.      #include "cl.h"
  376.  
  377.      CL_WELL_ENDOWED(int)
  378.  
  379.      #define intFile "ints.tmp"
  380.  
  381.      main()
  382.      {
  383.           time_t t;
  384.           srand((unsigned) time(&t));
  385.           CL<int> ci(CL_ANDS,15);
  386.           int i = 0;
  387.           while (ci.atInsNew(rand()%(ci.Nodes()+1),&i))
  388.                i++;
  389.           ci.save(intFile);
  390.           ci.allClr();
  391.           ci.load(intFile);
  392.           cout << "0-14 in random order: " << endl;
  393.           while (ci.atDelAsg(0,&i))
  394.                cout << i << endl;
  395.           return 0;
  396.      }
  397.  
  398.  
  399. The CL_WELL_ENDOWED() macro defines the requisite inline mediator
  400. functions that enable the CL<int> template invocation to make full use of
  401. the "int" data type's implicit operator>(int&), operator==(int&),
  402. operator=(int&), int(int&) and int() constructors, and
  403. operator<<(ostream&, int&) and operator>>(istream&, int&) functions
  404. (we'll learn more about this in the next chapter).   CL<int> in effect 
  405. generates a Container Lite wrapper class for integers.  Without
  406. CL_WELL_ENDOWED() we wouldn't be able to use the container's
  407. "(A)sg", "(N)ew", "(D)el" and stream (S)ave methods.  The CL_ANDS
  408. composite flag enables the container's "(A)sg", "(N)ew", "(D)el" and stream
  409. (S)ave methods.  Actually CL_ANDS is defined as the flags expression:
  410. (CL_ASG | CL_NEW | CL_DEL | CL_SAVE).  This container is also
  411. limited to a maximum of 15 items.
  412.  
  413. Next, the numbers zero through fourteen, i.e. the values of the variable i,
  414. are inserted at random locations in the "array."  Notice that the first
  415. parameter of the atInsNew() method is the "at" parameter.  Cells of the
  416. elastic array are indexed starting at zero just like a conventional C++ array. 
  417. Thus the last cell index of the elastic array is Nodes() - 1.  The "at" location
  418. selected for insertion is a random number between zero and Nodes().  Any
  419. attempt to insert at a location greater than Nodes()-1 is simply queued onto
  420. the end of the array.
  421.  
  422. The second parameter of the atInsNew() method is the "insert what"
  423. parameter.  The "New" suffix indicates that the item to be inserted must be
  424. cloned on the fly and that this clone is to be bound in the container instead
  425. of the original variable i in this case.  To bind means to store the address of
  426. a item in a cell of the container's elastic array.  Thus all item parameters of
  427. all Container Lite methods are pointers.  Hence we take the address of the
  428. variable i.
  429.  
  430. Most Container Lite methods return integers or pointers.  Such methods
  431. indicate failure by returning zero.  In our example atInsNew() returns a
  432. pointer to the newly cloned integer being bound.  When maxNodes is
  433. reached, i.e. 15, atInsNew() returns NULL indicating failure and the while
  434. loop terminates.  
  435.  
  436. Since the CL_SAVE flag is set (via CL_ANDS), the save() method is
  437. enabled and our container of integers is saved in the file "ints.tmp."  The
  438. complete state of the container is saved in the file, e.g. flags, maximum
  439. node setting, etcetera.  If you already have an open stream handle you can
  440. use the stream insertion operator instead of the save() method.
  441.  
  442. Since the CL_DEL flag is set (via CL_ANDS), the allClr() method calls
  443. allDel() instead of allRmv().  AllRmv() releases all binding addresses,
  444. removing their cells from the elastic array.  AllDel does the same thing
  445. except that the nodes are deleted upon removing with the delete operator via
  446. a call to the container's deleteD() protected scope virtual function.
  447.  
  448. There is no CL_LOAD flag since a container on a stream would never have
  449. the option of having a reset load flag.  The load() method calls allClr()
  450. before attempting to load the container from stream.  Since the loaded items
  451. are naturally dynamic, the CL_DEL flag is set if it isn't already.  The
  452. complete state of the container saved in the stream is restored.
  453.  
  454. The first parameter of atDelAsg() is the "at" parameter and the second is the
  455. "assign to what" parameter.  After assignment is made the original container
  456. node is deleted via a call to the container's deleteD() protected scope virtual
  457. function.  AtDelAsg() returns the address of the variable i each time until the
  458. container is empty at which time it returns NULL.  Both the CL_DEL and
  459. CL_ASG flags must be set for atDelAsg() to be enabled.
  460.  
  461. When leaving the scope of main() the container's destructor is automatically
  462. called.  If CL_DEL flag is set, allDel() is called instead of allRmv().  This
  463. is irrelevant in our example since all nodes have already been disposed of.
  464.  
  465. The logical basis for method naming is as follows.  The atInsNew() method
  466. performs the same operation as atIns() except the data is cloned on the fly
  467. and the clone is bound in the container instead of the variable i.  Again, to
  468. bind means to store the address of a node in a cell of the container's elastic
  469. array.  In either case a pointer to the bound node is returned to indicate
  470. success or NULL otherwise.  AtDelAsg() basically performs an atGetAsg(),
  471. (As)si(g)ning (copying) the outgoing node to the variable i, before
  472. performing an equivalent atDel() which is the same thing as an atRmv()
  473. except the outgoing node is (Del)eted and not just the cell (R)e(m)o(v)ed
  474. from the array.
  475.  
  476. If your compiler doesn't support templates you can use the "form 
  477. template" approach shown below instead.
  478.  
  479.  
  480.      // examp202.cpp -- link with cl.obj
  481.  
  482.      #include <stdlib.h> // rand(), srand()
  483.      #include <time.h>   // time_t, time()
  484.  
  485.      #define   ITEM  int
  486.      #define   CL_WELL_ENDOWED
  487.      #define   CL    CL_int
  488.      #include "cl.hf"
  489.  
  490.      #define intFile "ints.tmp"
  491.  
  492.      main()
  493.      {
  494.           time_t t;
  495.           srand((unsigned) time(&t));
  496.           CL_int ci(CL_ANDS,15);
  497.           int i = 0;
  498.           while (ci.atInsNew(rand()%(ci.Nodes()+1),&i))
  499.                i++;
  500.           ci.save(intFile);
  501.           ci.allClr();
  502.           ci.load(intFile);
  503.           cout << "0-14 in random order: " << endl;
  504.           while (ci.atDelAsg(0,&i))
  505.                cout << i << endl;
  506.           return 0;
  507.      }
  508.  
  509.  
  510. Notice that you must define ITEM as the parameter type that would
  511. normally be passed as the template parameter, i.e. "#define ITEM int" is
  512. the equivalent of passing "int" to "CL<>."  Likewise CL is defined as a
  513. new type name so that you use "CL_int" in place of "CL<int>."  You
  514. must define CL_WELL_ENDOWED so that the form template is
  515. customized to make fully use of all implicit or explicit overloaded
  516. operators for your data type.
  517.  
  518. The next example shows a container being used to sort a standard C++
  519. vector of strings.  Both the template and form template approaches are
  520. shown combined.
  521.  
  522.  
  523.      // examp203.cpp - link with cl.obj
  524.  
  525.      // #define CL_NO_TEMPLATES
  526.  
  527.      char *V[] = {
  528.           "Vectors of pointers can",
  529.           "be exploded into containers",
  530.           "for processing.  A container",
  531.           "can also be imploded into",
  532.           "a vector of pointers to",
  533.           "its nodes.",
  534.           0
  535.      };
  536.  
  537.      #include "cl.h"
  538.      #if defined(CL_NO_TEMPLATES)
  539.           #define ITEM_char
  540.           #include "cl.hf"
  541.      #endif
  542.  
  543.      #define vectorFile "vector.tmp"
  544.  
  545.      main()
  546.      {
  547.           CL_char v(V,0,CL_SAVE);  // explode constructor
  548.           cout << "\n\nExploded, unsorted vector ...\n\n";
  549.           for (unsigned i = 0; v[i]; i++)
  550.                cout << (char *) v[i] << "\n";
  551.           v.save(vectorFile);
  552.           v.allClr();
  553.           v.load(vectorFile);
  554.           v.sort ();    // sort
  555.           v.vector(V);  // implode
  556.           cout << "\n\nImploded, sorted vector ... \n\n";
  557.           for (i = 0; v[i]; i++)  
  558.                cout << v[i] << "\n";
  559.           return 0;
  560.      }
  561.  
  562.  
  563.  
  564. The Container Lite header file, i.e. cl.h, automatically defines
  565. CL_NO_TEMPLATES for compilers not yet supporting templates.  If
  566. your compiler doesn't support templates be sure to add its registration to
  567. the header file.  Comments in cl.h explain how.  In our example we also
  568. allow for the possibility of defining CL_NO_TEMPLATES.  This was
  569. done simply so that the form template approach could be tested with a
  570. template generating compiler.
  571.  
  572. Container Lite can automatically generate a template for a container of
  573. strings, i.e. CL_char.  With the form template approach you must signal
  574. the generation of container of strings by defining ITEM_char.  Normally
  575. you won't include code support for both the template and form template
  576. approaches.  It is shown here and in later examples so that you can
  577. contrast the two methods.
  578.  
  579. The vector() function will allocation a dynamic vector if one isn't
  580. provided as a parameter.  The overloaded subscript operator, i.e. [], is a
  581. convenient notation for calling atGet().  For details about any Container
  582. Lite member function or data be sure to refer to the reference chapter. 
  583. Remember, the examples given in this chapter are meant to give you a
  584. feel for Container Lite's different modes of operation, i.e. elastic array,
  585. stack-queue-deque, list, and sort-search-unique.  Thus the descriptions
  586. here don't focus on member function details.
  587.  
  588. You may have noticed by now that neither iostream.h nor iomanip.h is
  589. ever included in our examples.  That's because Container Lite header files
  590. already pull them in.  And string.h is automatically pulled in also.
  591.  
  592.  
  593. Stack - Queue - Deque
  594.  
  595.  
  596.  
  597. A stack provides LIFO (Last In, First Out) storage.  Container Lite has a
  598. complete set of stack methods.  These functions don't disturb the list
  599. behavior of a container, e.g. current node setting.  A queue provides
  600. FIFO (First In, First Out) storage.  And the deque (pronounced deck)
  601. provides FOLO (First Out, Last Out) storage.  Think of a deque as a
  602. combination stack-queue with the additional capability of being able to be
  603. popped from the rear.  The following is a list of the member functions
  604. supporting this stack-queue-deque behavior:
  605.  
  606.  
  607.      push()              pushNew()           pop()
  608.      popDel()            popDelAsg()         top()
  609.      topAsg()            insQ()              insQNew()
  610.      unQ()               unQDel()            unQDelAsg()
  611.      rear()              rearAsg()           operator<<()
  612.      operator>>()
  613.  
  614.  
  615. The example for this section uses a container of floats.  Again both the
  616. template and form template approaches are shown.
  617.  
  618.  
  619.      // examp204.cpp -- link with cl.obj
  620.  
  621.      // #define CL_NO_TEMPLATES
  622.  
  623.      #include "cl.h"
  624.      #if defined(CL_NO_TEMPLATES)
  625.           #define   ITEM  float
  626.           #define   CL_WELL_ENDOWED
  627.           #define   CL    CL_float
  628.           #include "cl.hf"
  629.      #else
  630.           CL_WELL_ENDOWED(float)
  631.           #define CL_float CL<float>
  632.      #endif
  633.  
  634.      #define floatFile "floats.tmp"
  635.  
  636.      main()
  637.      {
  638.           CL_float cf(CL_ANDS,10);
  639.           for (float i = 1.0; cf.pushNew(&i); i++);
  640.           cf.save(floatFile);
  641.           cf.allClr();
  642.           cf.load(floatFile);
  643.           cout << "Count to 10: " << endl;
  644.           while (cf.unQDelAsg(&i))
  645.                cout << i << endl;
  646.           return 0;
  647.      }
  648.  
  649.  
  650. CL_ANDS (equivalent to the CL_ASG, CL_NEW, CL_DEL, and
  651. CL_SAVE flags) is set in the constructor call this time.  Without
  652. CL_NEW being set, pushNew() would have been inhibited and all other
  653. member functions with the "New" suffix.  Without CL_ASG and
  654. CL_DEL being set, unQDelAsg() would also have been inhibited along
  655. with all other functions with "Asg" and/or "Del" in their names.  Without
  656. CL_SAVE the save() method would also have been inhibited.  The
  657. various flags are explained in detail in the reference chapter.
  658.  
  659. This container is limited to a maximum of 10 items.  That's because 10
  660. was passed to the constructor parameter named "maxNodes."  This can be
  661. changed at any time with a call to the setMaxNodes() function that you
  662. saw cataloged in the previous section.  The unQ() family of member
  663. functions work on the rear of the queue thus providing the deque
  664. behavior.
  665.  
  666. The container's destructor is automatically called just before "return 0;." 
  667. Since the previous while loop has already deleted all nodes there is
  668. nothing left to delete.  However, you should take note that if a container's
  669. CL_DEL flag is raised the destructor will attempt to delete any remaining
  670. nodes, otherwise the nodes are simply removed.  Thus you should
  671. typically segregate you containers into two groups: those with static nodes
  672. and those with dynamic.  You don't want to accidently delete a statically
  673. allocated node that is mingling among the dynamically allocated ones!  If
  674. you mix the two types it's safest not to set the CL_DEL flag since a
  675. compiler generated destructor call could then jump out and bite you!
  676.  
  677.  
  678. List
  679.  
  680.  
  681.  
  682. Any container can be treated as if it were a doubly linked list thereby
  683. providing sequential storage.  In a container's header structure a current
  684. node index is maintained that lets the list methods know where the
  685. insertion or removal is to take place.  The elastic array and
  686. stack-queue-list methods don't disturb this current node index unless of
  687. course it points to the cell that's being removed.  In that case the current
  688. node becomes undefined just like it was when the container was first
  689. constructed.  When a container is saved in a file the current node setting
  690. is saved too.  When the container is reloaded its previous current node
  691. setting is restored.  Here's a list of the list methods.
  692.  
  693.  
  694.      CurNode()           setCurNode()        ins()
  695.      insNew()            rmv()               del()
  696.      delAsg()            put()               putNew()
  697.      putAsg()            get()               getAsg()
  698.      next()              operator++()        nextAsg()
  699.      prev()              operator--()        prevAsg()
  700.  
  701.  
  702. Let's look at a little more rigorous, realistic example this time.  We'll
  703. create a container of Employees.  You'll learn more in the next chapter
  704. about using (form) templates for various data types.
  705.  
  706.  
  707.      // examp205.cpp -- link with cl.obj
  708.  
  709.      // #define CL_NO_TEMPLATES
  710.  
  711.      #include "cl.h"
  712.  
  713.      class Employee  {
  714.           char *name;
  715.           unsigned salary;
  716.           int cmp(const Employee& e) const;
  717.           static    Employee * THIS;
  718.           static    ostream& SHOW(ostream& os)
  719.           {
  720.              return os << "Employee name: "
  721.                 << setw(20)
  722.                 << (THIS->name? THIS->name : "n/a")
  723.                 << "\tsalary: " << THIS->salary;
  724.           }
  725.      public:
  726.           Employee(const char * name = 0,
  727.                unsigned salary = 0)
  728.           {
  729.                this->name = (name? strdup(name) : 0);
  730.                this->salary = salary;
  731.           }
  732.           Employee(const Employee& e)
  733.           {
  734.                name = (e.name? strdup(e.name) : 0);
  735.                salary = e.salary;
  736.           }
  737.           Employee& operator=(const Employee& e)
  738.           {
  739.                delete name;
  740.                name = (e.name? strdup(e.name) : 0);
  741.                salary = e.salary;
  742.                return *this;
  743.           }
  744.           int operator==(const Employee& e) const
  745.                { return !cmp(e); }
  746.           int operator>(const Employee& e) const
  747.                { return (cmp(e) > 0); }
  748.           ~Employee() { delete name; }
  749.           ostream& (*show())(ostream&)
  750.                { THIS = this; return SHOW; }
  751.           friend ostream& operator<<
  752.                (ostream& os, Employee& e)
  753.           { return os << &e.name << endm << e.salary; }
  754.           friend istream& operator>>
  755.                (istream& is, Employee& e)
  756.           { return is >> &e.name >> nextm >> e.salary; }
  757.      };
  758.  
  759.      Employee * Employee::THIS;
  760.  
  761.      int Employee::cmp(const Employee& e) const
  762.      {
  763.           if (!name)
  764.                if (!e.name)
  765.                     return 0;
  766.                else
  767.                     return -1;
  768.           else
  769.                if (!e.name)
  770.                     return 1;
  771.                else
  772.                   return strcmp(name,e.name);
  773.      }
  774.  
  775.  
  776.      #include "cl.h"
  777.      #if defined(CL_NO_TEMPLATES)
  778.           #define   ITEM      Employee
  779.           #define   CL_WELL_ENDOWED
  780.           #define   CL        CL_Employee
  781.           #include "cl.hf"
  782.      #else
  783.           CL_WELL_ENDOWED(Employee)
  784.           #define   CL_Employee       CL<Employee>
  785.      #endif
  786.  
  787.      #define EmployeeFile "employs.tmp"
  788.  
  789.      main()
  790.      {
  791.           CL_Employee cE(CL_ANDS);
  792.           cE.ins(new Employee("Doe, John",1000));
  793.           Employee E("Small, John",100);
  794.           cE.insQNew(&E);
  795.           cE.push(new Employee("Morgan, Maria",10000));
  796.           cE.save(EmployeeFile);
  797.           cE.allClr();
  798.           cE.load(EmployeeFile);
  799.           cE.sort();
  800.           cout << "\nEmployees in alphabetical order: "
  801.                << endl;
  802.           while (cE.nextAsg(&E))
  803.                cout << E.show() << endl;
  804.           return 0;
  805.      }
  806.  
  807.  
  808.  
  809. Thus far we have only seen strongly typed checked containers.  We could
  810. have just as easily used a type-less container which the next example
  811. demonstrates.  However we loose the use of "Asg" and "New" methods
  812. along with persistence.  After all, how can a container copy, clone, or
  813. store data that it knows nothing about?  Likewise how can it sort and
  814. search un-typed data?
  815.  
  816.  
  817.      // examp206.cpp -- link with cl.obj
  818.  
  819.      #include "cl.h"
  820.  
  821.      main()
  822.      {
  823.           cl s;
  824.           s.ins("in typeless containers!");
  825.           s.ins("Heterogenous data");
  826.           s.ins("can be easily bound");
  827.           // s.CurNode() == 2
  828.           s.insNew("This string won't appear!");
  829.           s.sort(CLcmPcast(strcmp,void));
  830.           // s.CurNode() == CL_NOTFOUND != 0
  831.           while (++s)
  832.                cout << (char *)s.get() << endl;
  833.           return 0;
  834.      }
  835.  
  836.  
  837. Notice that "cl" is a typeless container where as CL was used with
  838. templates.  The reason the fourth string won't appear is that neither the
  839. CL_NEW flag was set in the constructor call nor was the protected scope
  840. assignD() virtual function overridden during template generation to handle
  841. strings.  Notice that the sort() function takes an optional compare function
  842. pointer parameter.  The CLcmPcast() macro type casts the standard C
  843. library's strcmp() function pointer to one taking constant void pointer
  844. parameters.  Many times when working with non persistent heterogeneous
  845. data it is easiest to play fast and loose with a typeless container.
  846.  
  847. When the current node is rmv()'ed or del()'ed its predecessor is made the
  848. new current node.  For example, if the 5th node is rmv()'ed then
  849. CurNode() returns 4.  However, if the current node being 0 is rmv()'ed
  850. then the new current node remains 0 until there are no more nodes and
  851. the current node then becomes undefined, which is not 0!  When the
  852. current node is undefined, CurNode() returns the constant
  853. CL_NOTFOUND.  The operation of rmv() is designed to perform the
  854. inverse function of ins() which inserts after the current node making the
  855. new node current.  An ins() followed by rmv() will leave a list in its
  856. original state.
  857.  
  858.  
  859. Sort - Search - Unique
  860.  
  861.  
  862.  
  863. Containers can also be sorted and searched on a default or user supplied
  864. compare function.  You don't have to derive a new class for each
  865. different sort order!  Only template and "form template" generated
  866. containers have default compare functions (but only if the template
  867. parameter type has implicitly or explicitly defined relational operators or
  868. overrides the template's compare mediator function).  If a user defined
  869. compare function is registered it can persist also.  Please note that these
  870. methods affect the list's current node setting.
  871.  
  872.  
  873.      Sorted()            unSort()            setCmP()
  874.      ::Register_CmP()    CmP()               sort()
  875.      insSort()           insSortNew()        insUnique()
  876.      insUniqueNew()      findFirst()         operator[]
  877.      findNext()          findLast()          findPrev()
  878.      findAll()           tallyAll()
  879.  
  880.  
  881. With these methods you can build bags, sets, and even dictionaries which
  882. our next example will demonstrate.  One common challenge to any
  883. programmer is to produce an internationalized version of his/her
  884. application.  The following string resource suggests one approach.  In our
  885. implementation, the first word in a string is the token/key for the
  886. remainder of the string.  To change language versions, edit the remainder
  887. of each string.  Since all token/key - translation strings are to be located
  888. together the task should be easy.
  889.  
  890.  
  891.      // examp207.cpp -- link with cl.obj
  892.  
  893.      // #define CL_NO_TEMPLATES
  894.  
  895.      #include <ctype.h>
  896.  
  897.      #include "cl.h"
  898.      #if defined(CL_NO_TEMPLATES)
  899.           #define ITEM_char
  900.           #include "cl.hf"
  901.      #endif
  902.  
  903.  
  904.      class StrReSrc : CL_char  {
  905.           static unsigned ridx;
  906.           static int cmp
  907.                (const char * S1, const char * S2);
  908.      protected:
  909.           virtual  voidCmP cmPD(voidCmP cmP)
  910.           {
  911.                return (cmP? cmP : CLcmPcast(cmp,void));
  912.           }
  913.      public:
  914.           StrReSrc() : CL_char(CL_SAVE) {}
  915.           StrReSrc(const char * filename)
  916.                : CL_char(defaultConstruct)
  917.                { (void) CL_char::load(filename); }
  918.           int load(const char * filename)
  919.                { return CL_char::load(filename); }
  920.           int save(const char * filename)
  921.                { return CL_char::save(filename); }
  922.           int add(char * S)
  923.                { return (insUnique(S)?1:0); }
  924.           const char * operator[](const char * S)
  925.           {
  926.                return (findFirst(S)?
  927.                     &(((char *)get())[ridx])
  928.                          : "not found");
  929.           }
  930.           CL_char::allClr;
  931.           ~StrReSrc() { CL_char::destruct(); }
  932.      };
  933.  
  934.      unsigned StrReSrc::ridx;
  935.  
  936.      int StrReSrc::cmp(const char * S1, const char * S2)
  937.      {
  938.           for (ridx = 0; S1[ridx] && S2[ridx]; ridx++)
  939.                if (S1[ridx] != S2[ridx])
  940.                     break;
  941.                else if (isspace(S1[ridx]))
  942.                     // keys match
  943.                     return 0;
  944.           if (!S1[ridx] && S2[ridx] == ' ')
  945.                     return 0;
  946.           return S1[ridx] - S2[ridx];
  947.      }
  948.  
  949.      #define StrReSrcFile  "spanish.tmp"
  950.  
  951.      main()
  952.      {
  953.           StrReSrc sr;
  954.           sr.add("one uno");
  955.           sr.add("two dos");
  956.           sr.add("three tres");
  957.           sr.add("three wrong");
  958.           sr.save(StrReSrcFile);
  959.           sr.allClr();
  960.           sr.load(StrReSrcFile);
  961.           cout << "\nCount to three in Spanish: "
  962.                << endl;
  963.           cout << sr["one"] << endl;
  964.           cout << sr["two"] << endl;
  965.           cout << sr["three"] << endl;
  966.           return 0;
  967.      }
  968.  
  969.  
  970. The (form) template wrapper for strings is used to derive a string
  971. resource.  The cmPD() virtual function is then overridden to make our
  972. token/key compare function the default.  Notice how the default subscript
  973. operator, i.e. operator[](), is overloaded to allow the token/key to access
  974. the translation/value.  Hence we see that a dictionary action is really
  975. defined by the supplied compare function and overloaded access operator. 
  976. But recall a dictionary is essentially a set.  The add() function defined
  977. here assures us that only unique elements (token/keys) are allowed to
  978. belong to the string resource dictionary (set).   To become a bag the add()
  979. function would need to be changed to call insSort() instead of
  980. insUnique().  And to determine the count of a particular bag item one
  981. would call findAll().
  982.  
  983. Once a container is sorted, it will remain in a sorted state, as far as the
  984. container is concerned, until the compare function is changed or a new
  985. node is added without insSort???() or insUnique???().  However, it is
  986. possible for you to access a node, modifying the key value, without the
  987. container being aware that the sorted order has been spoiled.  Be sure to
  988. use UnSort() to let the container know if it is no longer sorted in these
  989. cases.
  990.  
  991. With other commercial and compiler bundled container libraries, you
  992. would have been forced to derived a new class for each different key.
  993.  
  994. A rework of examp207.cpp using the ANSI string class can be found in
  995. file examp208.cpp.  The file examp209.cpp demonstrates the use of a
  996. Container Lite container to read and modify an MS Windows style
  997. initialization file, e.g. *.ini.  A more extensive example that exercises the
  998. find???() family of member functions and alternate compare functions can
  999. be found in examp210.cpp which parses command line parameters. 
  1000. These file listings are not reproduced here because of space
  1001. considerations.
  1002.  
  1003.  
  1004. Iterators
  1005.  
  1006.  
  1007.  
  1008. You have already seen the Container Lite iterators sprinkled amongst the
  1009. previous sections.  We'll use this section to list them all in one convenient
  1010. place.  Notice that Container Lite iterators are integrated methods and
  1011. don't involve helper classes.
  1012.  
  1013. The following methods utilize a user supplied apply - detect - collect
  1014. function, leaving the list's current node setting undisturbed being atomic
  1015. in themselves:
  1016.  
  1017.  
  1018.      forEach()           detect()            select()
  1019.      reject()            collect()
  1020.  
  1021.  
  1022. The following methods affect the list's current node setting:
  1023.  
  1024.  
  1025.      next()              operator++()        nextAsg()
  1026.      prev()              operator--()        prevAsg()
  1027.  
  1028.  
  1029. The following methods affect the list's current node setting, utilizing the
  1030. internally stored compare function pointer for matching:
  1031.  
  1032.  
  1033.      findFirst()         operator[]          findNext()
  1034.      findLast()          findPrev()          findAll()
  1035.      tallyAll()
  1036.  
  1037.  
  1038. Our next example will show off the Smalltalk like iterators.  It is a
  1039. rework of examp205.cpp so only the pertinent portions are reproduced
  1040. here.
  1041.  
  1042.  
  1043.      // examp211.cpp -- link with cl.obj
  1044.      // rework of examp205.cpp
  1045.      // Show off Smalltalk like iterators
  1046.  
  1047.      ...
  1048.  
  1049.      class Employee  {
  1050.  
  1051.           ...
  1052.  
  1053.      public:
  1054.  
  1055.           ...
  1056.  
  1057.           Employee * operator()(const char * name)
  1058.           {
  1059.                delete this->name;
  1060.                this->name = (name? strdup(name) : 0);
  1061.                salary = 0;
  1062.                return this;
  1063.           }
  1064.  
  1065.           static int cmpName(const Employee * E1,
  1066.                const Employee * E2)
  1067.                { return strcmp(E1->name,E2->name); }
  1068.  
  1069.           static int detectName(Employee * E,
  1070.                void * name)
  1071.           { return (name? !strcmp(E->name,(char*)name) : 0); }
  1072.  
  1073.           static int detectSalaryGE(Employee * E,
  1074.                void * salary)
  1075.           { return (E->salary >= *(unsigned*)salary); }
  1076.  
  1077.           static void * collectSalaryGE(Employee *E,
  1078.                void * salary)
  1079.           { return ((E->salary >= (*(unsigned*)salary))?
  1080.           (void *) new unsigned(E->salary) : 0); }
  1081.  
  1082.  
  1083.      };
  1084.  
  1085.      ...
  1086.  
  1087.      #include "cl.h"
  1088.  
  1089.      #if defined(CL_NO_TEMPLATES)
  1090.           #define   ITEM      Employee
  1091.           #define   CL_WELL_ENDOWED
  1092.           #define   CL        CL_Employee
  1093.           #include "cl.hf"
  1094.      #else
  1095.           CL_WELL_ENDOWED(Employee)
  1096.           #define   CL_Employee       CL<Employee>
  1097.      #endif
  1098.  
  1099.      #if defined(CL_NO_TEMPLATES)
  1100.           #define   ITEM     unsigned
  1101.           #define   CL_WELL_ENDOWED
  1102.           #define   CL       CL_unsigned
  1103.           #include "cl.hf"
  1104.      #else
  1105.           CL_WELL_ENDOWED(unsigned)
  1106.           #define   CL_unsigned    CL<unsigned>
  1107.      #endif
  1108.  
  1109.  
  1110.      #define EmployeeFile "employs.tmp"
  1111.  
  1112.      main()
  1113.      {
  1114.           CL_Employee cE(CL_ANDS);
  1115.           cE.ins(new Employee("Doe, John",1000));
  1116.           Employee E("Mitchell, Allen",100);
  1117.           cE.insQNew(&E);
  1118.           cE.push(new Employee("Morgan, Maria",10000));
  1119.           cE.insQ(new Employee("Zorro", 200));
  1120.           cE.save(EmployeeFile);
  1121.           cE.allClr();
  1122.           cE.load(EmployeeFile);
  1123.           cE.sort();
  1124.           cout << "\nEmployees in alphabetical order: "
  1125.                << endl;
  1126.           while (cE.nextAsg(&E))
  1127.                cout << E.show() << endl;
  1128.           Employee *Eptr;
  1129.           unsigned i, salary = 500;
  1130.           char name[] = "Morgan, Maria";
  1131.           cout << "\nFind employee: "
  1132.                << name << endl;
  1133.           if (cE.detect(Eptr,Employee::detectName,name))
  1134.                cout << Eptr->show() << endl;
  1135.           if (cE.detect(i,Employee::detectName,name))
  1136.                cout << cE[i]->show() << endl;
  1137.           cout << "\nFind first employe with salary >= "
  1138.                << salary << endl;
  1139.           if (cE.detect(Eptr,Employee::detectSalaryGE,&salary))
  1140.                cout << Eptr->show() << endl;
  1141.           cout << "\nFind all employees with salaries >= "
  1142.                << salary << endl;
  1143.           CL_Employee cE2;
  1144.           if (cE.select(cE2,Employee::detectSalaryGE,&salary))
  1145.                while (++cE2)
  1146.                     cout << ((Employee*)cE2)->show() << endl;
  1147.           cout << "\nList all salaries >= " << salary << endl;
  1148.           CL_unsigned cs;
  1149.           if (cE.collect((cl&)cs,Employee::collectSalaryGE,&salary))
  1150.                while (++cs)
  1151.                     cout << *(unsigned *)cs << endl;
  1152.           cE2.allClr();
  1153.           cout << "\nFind all employees with name >= "
  1154.                << name << endl;
  1155.           cE.setCmP(Employee::cmpName);
  1156.           if (cE.findAll(cE2,E(name),cl::GE))
  1157.                while (++cE2)
  1158.                     cout << ((Employee*)cE2)->show() << endl;
  1159.           return 0;
  1160.      }
  1161.  
  1162.  
  1163. Summary
  1164.  
  1165.  
  1166.  
  1167. The easiest way to conceptualize Container Lite operations is to classify
  1168. its method behaviors into five categories: elastic array, stack-queue-
  1169. deque, list, sort-search-unique, and iterator.  Every container has an
  1170. inherent current node setting associated with its list behavior which is also
  1171. modified by its sort-search-unique methods but left undisturbed by the
  1172. elastic array and stack-queue-deque methods.  Since Container Lite
  1173. iterator operations are basically atomic in nature, separate helper classes
  1174. are not necessary.
  1175.  
  1176.                                                                   Chapter 3
  1177.  
  1178.  
  1179.                                                        Strong Type Checking
  1180.  
  1181.  
  1182. Container Lite's foundation class is named "cl."  The "cl" class used by
  1183. itself performs no type checking on the data being bound within.  These
  1184. typeless containers are useful for quick and dirty handling of
  1185. heterogeneous data types.  The following code snipet shows a typeless,
  1186. i.e. not type checked at compile time, container for your particular data
  1187. type.
  1188.  
  1189.  
  1190.      #include "cl.h"
  1191.  
  1192.      ...
  1193.  
  1194.      {
  1195.           cl  containerOfYourDataType;
  1196.           ...
  1197.  
  1198.  
  1199. However, by using a template the resultant container wrapper class can be
  1200. made to impose strong type checking for any type of data at compile
  1201. time, thereby restricting the use of that container to a particular data type. 
  1202. To build a strongly type checked container for your particular data type
  1203. you should code something like:
  1204.  
  1205.  
  1206.      #include "cl.h"
  1207.  
  1208.      ...
  1209.  
  1210.      {
  1211.           CL<YourDataType>  containerOfYourDataType;
  1212.           ...
  1213.  
  1214.  
  1215. Even if your compiler doesn't support templates, strong type checking can
  1216. still be achieved with Container Lite's "form templates."
  1217.  
  1218.  
  1219.      #include "cl.h"
  1220.  
  1221.      ...
  1222.  
  1223.      #define ITEM  YourDataType
  1224.      #define CL CL_YourDataType
  1225.  
  1226.      ...
  1227.  
  1228.  
  1229.      {
  1230.           CL_YourDataType  containerOfYourDataType;
  1231.           ...
  1232.  
  1233.  
  1234. You have already seen all three approaches in the examples of the
  1235. previous chapter.  In some of those examples both the template and
  1236. "form" template approaches were combined for the sake of contrasting
  1237. them.
  1238.  
  1239.  
  1240.      #include "cl.h"
  1241.      #if defined(CL_NO_TEMPLATES)
  1242.           #define   ITEM     YourDataType
  1243.           #define   CL       CL_YourDataType
  1244.           #include "cl.hf"
  1245.      #else
  1246.           #define   CL_YourDataType   CL<YourDataType>
  1247.      #endif
  1248.  
  1249.      ...
  1250.  
  1251.      {
  1252.           CL_YourDataType  containerOfYourDataType;
  1253.           ...
  1254.  
  1255.  
  1256. Recalling examp211.cpp we observe that "cl.hf" can be included a
  1257. multitude of times to facilitate the "form" template generation of various
  1258. wrappers, e.g. CL_Employee and CL_unsigned.
  1259.  
  1260.  
  1261.      #include "cl.h"
  1262.  
  1263.      #if defined(CL_NO_TEMPLATES)
  1264.           #define   ITEM      Employee
  1265.           #define   CL_WELL_ENDOWED
  1266.           #define   CL        CL_Employee
  1267.           #include "cl.hf"
  1268.      #else
  1269.           CL_WELL_ENDOWED(Employee)
  1270.           #define   CL_Employee       CL<Employee>
  1271.      #endif
  1272.  
  1273.      #if defined(CL_NO_TEMPLATES)
  1274.           #define   ITEM     unsigned
  1275.           #define   CL_WELL_ENDOWED
  1276.           #define   CL       CL_unsigned
  1277.           #include "cl.hf"
  1278.      #else
  1279.           CL_WELL_ENDOWED(unsigned)
  1280.           #define   CL_unsigned    CL<unsigned>
  1281.      #endif
  1282.  
  1283.  
  1284. The typeless container we saw at the beginning of the chapter can not call
  1285. a destructor for a node before deleting it because the node's data type
  1286. isn't known.  This is something you should keep in mind before setting
  1287. the CL_DEL flag that enables the "Del" methods and by the way forces
  1288. cl::~cl() to call allDel() on any nodes remaining in the container!  Both
  1289. the template and "form" template container wrapper classes do call a
  1290. node's destructor before deleting it since the data type is known.
  1291.  
  1292.  
  1293. Additional Functionality
  1294.  
  1295.  
  1296.  
  1297. A generic type checked container can not use the "Asg", "New", and
  1298. save() methods unless more information about the data type being bound
  1299. is known.  The methods are simply "turned off" in the generic wrapper
  1300. returning a failure condition regardless of the settings of the CL_ASG,
  1301. CL_NEW, and CL_SAVE flags.
  1302.  
  1303. For example, a container wrapper can be generated that automatically
  1304. compares your data within the insUnique(), sort(), and find???() methods
  1305. without a user supplied compare function.  How can these methods do
  1306. this?  Only if your data type has an overloaded equality operator, i.e.
  1307.  
  1308.           int ITEM::operator==(const ITEM&) const;
  1309.  
  1310.      or
  1311.  
  1312.           friend int operator==(const ITEM&, const ITEM&); 
  1313.  
  1314.      and an overloaded greater than operator, i.e.
  1315.  
  1316.           int ITEM::operator>(const ITEM&) const;
  1317.  
  1318.      or
  1319.  
  1320.           friend int operator>(const ITEM&, const ITEM&);
  1321.  
  1322.  
  1323. and only if the template or form is notified before the wrapper is
  1324. generated.  For example, if you code:
  1325.  
  1326.  
  1327.      #include "cl.h"
  1328.      #if defined(CL_NO_TEMPLATES)
  1329.           #define   ITEM     YourDataType
  1330.           #define   CL_EQ_GT_OPS
  1331.           #define   CL       CL_YourDataType
  1332.           #include "cl.hf"
  1333.      #else
  1334.           CL_EQ_GT_OPS(YourDataType)
  1335.           #define   CL_YourDataType   CL<YourDataType>
  1336.      #endif
  1337.  
  1338.      ...
  1339.  
  1340.      {
  1341.           CL_YourDataType  containerOfYourDataType;
  1342.           ...
  1343.           containerOfYourDataType.sort();
  1344.  
  1345.  
  1346. the sort() method can now be successfully called without specifying a
  1347. compare function.  Of course you can still change the container's internal
  1348. compare function at will to facilitate sorting or searching on various keys
  1349. but a default compare function is now available when required.  This
  1350. default compare function is in effect defined by YourDataType's
  1351. operator==() and operator>() overloaded functions.
  1352.  
  1353. A container wrapper can also be generated so that your data can be
  1354. copied on the fly.  For example, the nextAsg(&targetObject) iterator will
  1355. advance the list's current node pointer to the next node and assign that
  1356. node to the target object specified.  Your data type must have an
  1357. overloaded assignment operator however, i.e.
  1358.  
  1359.  
  1360.           ITEM& ITEM::operator=(const ITEM&);
  1361.  
  1362.  
  1363. You could then code something like:
  1364.  
  1365.  
  1366.      #include "cl.h"
  1367.      #if defined(CL_NO_TEMPLATES)
  1368.           #define   ITEM     YourDataType
  1369.           #define   CL_ASSIGN_OP
  1370.           #define   CL       CL_YourDataType
  1371.           #include "cl.hf"
  1372.      #else
  1373.           CL_ASSIGN_OP(YourDataType)
  1374.           #define   CL_YourDataType   CL<YourDataType>
  1375.      #endif
  1376.  
  1377.      ...
  1378.  
  1379.      {
  1380.           CL_YourDataType containerOfYourDataType(CL_ASG);
  1381.           ...
  1382.           YourDataType targetObject;
  1383.           for (containerOfYourDataType.setCurNode();
  1384.                containerOfYourDataType.nextAsg(targetObject);
  1385.                /* no reinit */ )
  1386.                targetObject.DoSomething();
  1387.           ...
  1388.  
  1389.  
  1390. Notice that the CL_ASG flag must still be set in the constructor call to
  1391. enable the "Asg" methods.  You can of course call the setFlags(CL_ASG)
  1392. member function instead.  In the example, the setCurNode() function sets
  1393. the list's current node to be undefined so that the first call to nextAsg()
  1394. returns a pointer to the container's first node and also copies the first
  1395. node's contents to "targetObject" via the overloaded assignment operator. 
  1396. When nextAsg() falls off the end of the list it returns NULL.
  1397.  
  1398. Likewise a container wrapper can be generated so that your data can be
  1399. cloned on the fly allowing you to bind the clone instead of the original. 
  1400. But your data type must have a copy initializer constructor, i.e.
  1401.  
  1402.  
  1403.           ITEM::ITEM(const ITEM&);
  1404.  
  1405.  
  1406. Since the "unsigned" data type has an implicit copy initializer we could
  1407. code the following:
  1408.  
  1409.  
  1410.      #include "cl.h"
  1411.      #if defined(CL_NO_TEMPLATES)
  1412.           #define   ITEM     unsigned
  1413.           #define   CL_COPYINIT
  1414.           #define   CL       CL_unsigned
  1415.           #include "cl.hf"
  1416.      #else
  1417.           CL_COPYINIT(unsigned)
  1418.           #define   CL_unsigned    CL<unsigned>
  1419.      #endif
  1420.  
  1421.      ...
  1422.  
  1423.      {
  1424.           CL_unsigned  containerOfUnsigned(CL_NEW,10);
  1425.           ...
  1426.           for (unsigned i = 0;
  1427.                containerOfUnsigned.insQNew(&i);
  1428.                i++);
  1429.           ...
  1430.  
  1431.  
  1432. Now you can use the container's "New" methods provided of course the
  1433. CL_NEW flag is set.  The insQNew() method first clones the unsigned
  1434. variable "i" and then inserts this clone into the queue, binding its address. 
  1435. The for loop terminates after ten nodes have been queued because the
  1436. container is limited to a maximum of ten nodes in the constructor call.
  1437.  
  1438. A strongly type checked container can also be made to persist, i.e. stored
  1439. on a stream as a file, provided it has
  1440.  
  1441.  
  1442.      an overloaded stream insertion operator, i.e.
  1443.  
  1444.           friend ostream& operator<<(ostream&,ITEM&);
  1445.  
  1446.      an overloaded stream extraction operator, i.e.
  1447.  
  1448.           friend istream& operator>>(istream&,ITEM&);
  1449.  
  1450.      and a default constructor, i.e.
  1451.  
  1452.           ITEM::ITEM();
  1453.  
  1454.  
  1455. In this case you could now code the following:
  1456.  
  1457.  
  1458.      #include "cl.h"
  1459.      #if defined(CL_NO_TEMPLATES)
  1460.           #define   ITEM     YourDataType
  1461.           #define   CL_STRM_OPS
  1462.           #define   CL       CL_YourDataType
  1463.           #include "cl.hf"
  1464.      #else
  1465.           CL_STRM_OPS(YourDataType)
  1466.           #define   CL_YourDataType   CL<YourDataType>
  1467.      #endif
  1468.  
  1469.      ...
  1470.  
  1471.      {
  1472.           CL_YourDataType  containerOfYourDataType(CL_SAVE);
  1473.           ...
  1474.           // 1.
  1475.           containerOfYourDataType.save("pathname");
  1476.           containerOfYourDataType.allClr();
  1477.           if (containerOfYourDataType.load("pathname"))
  1478.                // container same state as in 1. above
  1479.           ...
  1480.  
  1481.  
  1482. Of course the CL_SAVE flag must be set in order to save the container in
  1483. a file.  If you have an already opened stream handle you can use the
  1484. stream insertion and extraction operators with the container itself instead
  1485. of the save() and load() methods.  Please don't confuse these operators,
  1486. which are automatically generated by the template or form, with those
  1487. which you must supply for your data type.  If a container has a user
  1488. defined compare function set and it has been registered before the
  1489. container is saved, it will be automatically restored when the container is
  1490. reloaded from the file.  Any default compare function is always restored. 
  1491. Likewise the list's current node setting will persist also.
  1492.  
  1493.  
  1494. A Well Endowed Data Type
  1495.  
  1496.  
  1497.  
  1498. If your data type has all the overloaded operators and constructors, etc.
  1499. outlined above then your data type is well endowed as far as Container
  1500. Lite is concerned.  A fully featured container, i.e. one with the "Asg",
  1501. "New", and save() methods operational along with a default compare
  1502. function for the sort-search-unique methods, can then be generated as
  1503. shown in the following code.
  1504.  
  1505.  
  1506.      #include "cl.h"
  1507.      #if defined(CL_NO_TEMPLATES)
  1508.           #define   ITEM     YourDataType
  1509.           #define   CL_WELL_ENDOWED
  1510.           #define   CL       CL_YourDataType
  1511.           #include "cl.hf"
  1512.      #else
  1513.           CL_WELL_ENDOWED(YourDataType)
  1514.           #define   CL_YourDataType   CL<YourDataType>
  1515.      #endif
  1516.  
  1517.      ...
  1518.  
  1519.      {
  1520.           CL_YourDataType  containerOfYourDataType(CL_ANDS);
  1521.           ...
  1522.  
  1523.  
  1524. The CL_ANDS flag is actually a combination of flags being defined as
  1525. (CL_ASG | CL_NEW | CL_DEL | CL_SAVE) thus the container
  1526. constructed above has all its available methods enabled.  By the way,
  1527. please don't forget if you are always going to have templates available to
  1528. you with the compilers you'll be using you can simply code:
  1529.  
  1530.  
  1531.      CL_WELL_ENDOWED(YourDataType)
  1532.  
  1533.      ...
  1534.  
  1535.      {
  1536.           CL<YourDataType>  containerOfYourDataType(CL_ANDS);
  1537.           ...
  1538.  
  1539.  
  1540. You can see how this works when you consider that the template version
  1541. of the CL_WELL_ENDOWED() macro is defined as:
  1542.  
  1543.  
  1544.      #define CL_WELL_ENDOWED(ITEM) \
  1545.           CL_EQ_GT_OPS(ITEM)       \
  1546.           CL_ASSIGN_OP(ITEM)       \
  1547.           CL_COPYINIT(ITEM)        \
  1548.           CL_STRM_OPS(ITEM)
  1549.  
  1550.  
  1551. The "form" template is a bit more complicated but works essentially the
  1552. same way.
  1553.  
  1554. Let's review the requisite overloaded operators, etc. for a data type to be
  1555. considered well endowed.   It must have:
  1556.  
  1557.  
  1558.      1. an overloaded equality operator, i.e.
  1559.  
  1560.           int ITEM::operator==(const ITEM&) const;
  1561.  
  1562.         or
  1563.  
  1564.           friend int operator==(const ITEM&, const ITEM&); 
  1565.  
  1566.         and an overloaded greater than operator, i.e.
  1567.  
  1568.           int ITEM::operator>(const ITEM&) const;
  1569.  
  1570.         or
  1571.  
  1572.           friend int operator>(const ITEM&, const ITEM&);
  1573.  
  1574.      2. an overloaded assignment operator, i.e.
  1575.  
  1576.           ITEM& ITEM::operator=(const ITEM&);
  1577.  
  1578.      3. a copy initializer constructor, i.e.
  1579.  
  1580.           ITEM::ITEM(const ITEM&);
  1581.  
  1582.      4. an overloaded stream insertion operator, i.e.
  1583.  
  1584.           friend ostream& operator<<(ostream&,ITEM&);
  1585.  
  1586.      5. an overloaded stream extraction operator, i.e.
  1587.  
  1588.           friend istream& operator>>(istream&,ITEM&);
  1589.  
  1590.         and a default constructor, i.e.
  1591.  
  1592.           ITEM::ITEM();
  1593.  
  1594.      and
  1595.  
  1596.      6. a destructor if one is required, i.e.
  1597.  
  1598.           ITEM::~ITEM();
  1599.  
  1600.  
  1601. Now let's look at a well endowed string class example.
  1602.  
  1603.  
  1604.      // examp301.cpp -- link with cl.obj
  1605.  
  1606.      // #define CL_NO_TEMPLATES
  1607.  
  1608.      #include "cl.h"
  1609.  
  1610.  
  1611.      class String {
  1612.  
  1613.           char *str;
  1614.           size_t len;
  1615.           int cmp(const String& cs) const;
  1616.  
  1617.      public:
  1618.  
  1619.           String(const char * s = (const char *)0)
  1620.           {
  1621.                str = (s? len = strlen(s), strdup(s)
  1622.                     : (char *)(len = 0));
  1623.           }
  1624.           String(const String& s)
  1625.           {
  1626.                str = (((len = s.len) != 0)?
  1627.                     strdup(s.str) : (char *)0);
  1628.           }
  1629.           ~String() { delete str; }
  1630.           String& operator=(const String& s)
  1631.           {
  1632.                delete str;
  1633.                str = (((len = s.len) != 0)?
  1634.                     strdup(s.str) : (char *)0);
  1635.                return *this;
  1636.           }
  1637.           inline int operator==(const String& cs) const
  1638.                { return !cmp(cs); }
  1639.           inline int operator> (const String& cs) const
  1640.                { return (cmp(cs) > 0); }
  1641.           operator const char *()  { return str; }
  1642.           friend ostream& operator<<
  1643.            (ostream& os, String& s)
  1644.           { return os << &s.str; }
  1645.           friend istream& operator>>
  1646.                (istream& is, String& s)
  1647.           {
  1648.                is >> &s.str;
  1649.                s.len = (s.str? strlen(s.str) : 0);
  1650.                return is;
  1651.           }
  1652.      };
  1653.  
  1654.      int String::cmp(const String& cs) const
  1655.      {
  1656.           if (!str)
  1657.                if (!cs.str)
  1658.                     return 0;
  1659.                else
  1660.                     return -1;
  1661.           else
  1662.                if (!cs.str)
  1663.                     return 1;
  1664.                else
  1665.                     return strcmp(str,cs.str);
  1666.      }
  1667.  
  1668.  
  1669.      #ifdef CL_NO_TEMPLATES
  1670.           #define    ITEM     String
  1671.           #define    CL_WELL_ENDOWED
  1672.           #define    CL       CL_String
  1673.           #include  "cl.hf"
  1674.      #else
  1675.           CL_WELL_ENDOWED(String)
  1676.           #define    CL_String CL<String>
  1677.      #endif
  1678.  
  1679.      #define StrFile "strings.tmp"
  1680.  
  1681.      main()
  1682.      {
  1683.           CL_String cS(CL_ANDS);
  1684.           String s("can be");
  1685.           cS.insNew(&s);
  1686.           cS.ins(new String("tamed!"));
  1687.           cS.insQ(new String("Now strings"));
  1688.           cS.sort();
  1689.           cS.save(StrFile);
  1690.           cS.allClr();
  1691.           cS.load(StrFile);
  1692.           while (cS.popDelAsg(&s))
  1693.                cout << (const char *)s << endl;
  1694.           return 0;
  1695.      }
  1696.  
  1697.  
  1698. For traditional C/C++ strings, i.e. zero terminated char arrays, there is
  1699. a special wrapper already prepared for your use.  To use it simply code:
  1700.  
  1701.  
  1702.      #include "cl.h"
  1703.      #if defined(CL_NO_TEMPLATES)
  1704.           #define ITEM_char
  1705.           #include "cl.hf"
  1706.      #endif
  1707.  
  1708.      ...
  1709.  
  1710.      {
  1711.           CL_char  strings;
  1712.           ...
  1713.  
  1714.  
  1715. You saw this approach used in examp203.cpp and examp207.cpp.  If you
  1716. are only using a template supporting compiler the code would be simply:
  1717.  
  1718.  
  1719.      #include "cl.h"
  1720.  
  1721.      ...
  1722.  
  1723.      {
  1724.           CL_char strings;
  1725.           ...
  1726.  
  1727.  
  1728. This C/C++ string wrapper does not, however, enable you to use the
  1729. "Asg" methods.
  1730.  
  1731.  
  1732. Deficient Data Types
  1733.  
  1734.  
  1735.  
  1736. What do you do if your data is deficient in one or more of the six
  1737. requisites necessary to be considered well endowed yet you need the
  1738. additional functionality of the "Asg", "New", and save() methods?  If you
  1739. don't have the source code, i.e. the class belongs to some commercial
  1740. library, you can't just go back and modify the source.  The best way is to
  1741. derive a new class with the necessary functionality.  However, there are
  1742. other ways to modify the generated wrapper.  If your data is of a fixed
  1743. size with no embedded pointers, virtual functions or virtual bases, then
  1744. performing bytewise operations can provide a quick and dirty fix.  The
  1745. next example reworks examp205.cpp to allow bytewise operations to be
  1746. meaningful - notice that what was previously a string pointer is now a
  1747. fixed size char array.
  1748.  
  1749.  
  1750.      // examp302.cpp -- link with cl.obj
  1751.  
  1752.      // #define CL_NO_TEMPLATES
  1753.  
  1754.      #include "cl.h"
  1755.  
  1756.      class Employee  {
  1757.           char name[20];
  1758.           unsigned salary;
  1759.           static    Employee * THIS;
  1760.           static    ostream& SHOW(ostream& os)
  1761.           {
  1762.              return os << "Employee name: "
  1763.                 << setw(20)
  1764.                 << (THIS->name? THIS->name : "n/a")
  1765.                 << "\tsalary: " << THIS->salary;
  1766.           }
  1767.      public:
  1768.           Employee(const char * name = 0,
  1769.                unsigned salary = 0)
  1770.           {
  1771.                if (name)
  1772.                     strncpy(this->name,name,
  1773.                          sizeof(Employee::name));
  1774.                else
  1775.                     this->name[0] = '\0';
  1776.                this->salary = salary;
  1777.           }
  1778.           ostream& (*show())(ostream&)
  1779.                { THIS = this; return SHOW; }
  1780.      };
  1781.  
  1782.      Employee * Employee::THIS;
  1783.  
  1784.      #define EmployeeFile "employs.tmp"
  1785.  
  1786.      #ifdef CL_NO_TEMPLATES
  1787.           #define ITEM Employee
  1788.           #define CL_CMP_BYTES
  1789.           #define CL_ASSIGN_BYTES
  1790.           #define CL_CLONE_BYTES
  1791.           #define CL_STRM_BYTES
  1792.           #define CL CL_Employee
  1793.           #include "cl.hf"
  1794.      #else
  1795.           CL_CMP_BYTES(Employee)
  1796.           CL_ASSIGN_BYTES(Employee)
  1797.           CL_CLONE_BYTES(Employee)
  1798.           CL_STRM_BYTES(Employee)
  1799.           #define CL_Employee CL<Employee>
  1800.      #endif
  1801.  
  1802.  
  1803.      main()
  1804.      {
  1805.           CL_Employee cE(CL_ANDS);
  1806.           cE.ins(new Employee("Doe, John",1000));
  1807.           Employee E("Small, John",100);
  1808.           cE.insQNew(&E);
  1809.           cE.push(new Employee("Morgan, Maria",10000));
  1810.           cE.save(EmployeeFile);
  1811.           cE.allClr();
  1812.           cE.load(EmployeeFile);
  1813.           cE.sort();
  1814.           cout << "\nEmployees in alphabetical order: "
  1815.                << endl;
  1816.           while (cE.nextAsg(&E))
  1817.                cout << E.show() << endl;
  1818.           return 0;
  1819.      }
  1820.  
  1821.  
  1822. The CL_CMP_BYTES macro causes the template to generate a default
  1823. compare function that performs a byte by byte compare of two Employee
  1824. objects.  If an Employee object had embedded data pointers, the data
  1825. pointed to wouldn't be compared, just the pointer values.
  1826.  
  1827. The CL_ASSIGN_BYTES macro causes the "Asg" methods to copy
  1828. sizeof(Employee) bytes.  Again if an Employee object had embedded data
  1829. pointers problems would arise, i.e. the target object wouldn't delete those
  1830. pointers before overwriting them.  After the copying is completed the
  1831. target object would end up pointing to the source object's data instead of
  1832. a cloned copy.  If Employee had a virtual base class, the internal pointer
  1833. of the target object would end up pointing to the virtual base of the source
  1834. object.  Overwriting the virtual function table pointer may or may not
  1835. cause complications depending on your compiler's implementation and
  1836. options setting (dangerous ground to tread on).
  1837.  
  1838. The CL_CLONE_BYTES macro enables the "New" methods to also copy
  1839. sizeof(Employee) bytes.  Any suballocated data pointed to by the source
  1840. object wouldn't get cloned, but rather instead the cloned target and
  1841. original source would both end up pointing to the same data again.
  1842.  
  1843. The CL_STRM_BYTES macro allows the save() method to write
  1844. sizeof(Employee) bytes on a stream.  If Employee has any embedded
  1845. pointers, i.e. data, virtual base, or virtual function table you are going to
  1846. have problems when reloading.
  1847.  
  1848. Except for the most simple classes it's best not to use the bytewise
  1849. approach.  If these restrictions are met and the situation dictates, the
  1850. bytewise approach provides an easy fix.  You can of course mix the
  1851. previous macros, e.g. CL_EQ_GT_OPS, CL_ASSIGN_OP, etc., with any
  1852. of the bytewise macros in an unambiguous fashion.  For example,
  1853. suppose your data type did not have an overloaded assignment operator
  1854. but it did have a copy initializer.  You could then code something like:
  1855.  
  1856.  
  1857.      #include "cl.h"
  1858.      #if defined(CL_NO_TEMPLATES)
  1859.           #define ITEM  YourDataType
  1860.           #define CL_ASSIGN_BYTES
  1861.           #define CL_COPYINIT
  1862.           #define CL  CL_YourDataType
  1863.           #include "cl.hf"
  1864.      #else
  1865.           CL_ASSIGN_BYTES(YourDataType)
  1866.           CL_COPYINIT(YourDataType)
  1867.           #define CL_YourDataType  CL<YourDataType>
  1868.      #endif
  1869.  
  1870.      ...
  1871.  
  1872.      {
  1873.           CL_YourDataType  containerOfYourDataType;
  1874.           YourDataType  ydt(???);
  1875.           ...
  1876.           containerOfYourDataType.pushNew(&ydt);
  1877.           ...
  1878.           containerOfYourDataType.topAsg(&ydt);
  1879.           ...
  1880.  
  1881.  
  1882. There is a safer way to make up for your data's deficiencies which we'll
  1883. take a look at next.
  1884.  
  1885.  
  1886. Mediator Functions
  1887.  
  1888.  
  1889.  
  1890. Container Lite uses inline mediator functions to configure a template or
  1891. form to generate a wrapper class with additional functionality, i.e.
  1892. meaningful "Asg", "New", "Del", and save() methods, etc..  The
  1893. template or form automatically generates inline mediator function
  1894. definitions which in turn are used to configure the generated wrapper for
  1895. your data type.  (The inline mediator functions are specifically used to
  1896. define the overloaded virtual functions of the generated wrapper which
  1897. you'll see more of in the next chapter.)  The macros outlined in the
  1898. previous sections cause the appropriate inline mediator functions to be
  1899. defined.  Instead of using these macros you can also manually override
  1900. the mediator functions directly to mitigate the deficiencies of your data
  1901. type.
  1902.  
  1903. To see one reason you may need to take this approach consider a class
  1904. that has overloaded stream operators that were designed to be used for
  1905. console display purposes.  These would most likely be totally unsuitable
  1906. for file operations.  If you want a container of these objects to persist you
  1907. will have to derive a new class with stream operators designed for file
  1908. operations.  Again there is a quick and dirty solution if the need arises. 
  1909. Let's see a rework for a seriously deficient Employee structure.
  1910.  
  1911.  
  1912.      // examp303.cpp -- link with cl.obj
  1913.      // #define CL_NO_TEMPLATES
  1914.  
  1915.      #include "cl.h"
  1916.  
  1917.      struct Employee  {
  1918.           char *name;
  1919.           unsigned salary;
  1920.           Employee(const char * name,
  1921.                unsigned salary = 0)
  1922.           {
  1923.                this->name = (name? strdup(name) : 0);
  1924.                this->salary = salary;
  1925.           }
  1926.           ~Employee() { delete name; }
  1927.           friend ostream& operator<<
  1928.                (ostream& os, Employee& e)
  1929.           {
  1930.              return os << "Employee name: "
  1931.                 << setw(20)
  1932.                 << (e.name? e.name : "n/a")
  1933.                 << "\tsalary: " << e.salary;
  1934.           }
  1935.      };
  1936.  
  1937.      int CL_cmpD(const Employee * E1, const Employee * E2)
  1938.      {
  1939.           if (!E1->name)
  1940.                if (!E2->name)
  1941.                     return 0;
  1942.                else
  1943.                     return -1;
  1944.           else
  1945.                if (!E2->name)
  1946.                     return 1;
  1947.                else
  1948.                  return strcmp(E1->name,E2->name);
  1949.      }
  1950.  
  1951.      inline void * CL_assignD
  1952.           (Employee * D, const Employee * S,
  1953.           unsigned /* flags */)
  1954.      {
  1955.           delete D->name;
  1956.           D->name = (S->name?
  1957.                strdup(S->name) : 0);
  1958.           D->salary = S->salary;
  1959.           return D;
  1960.      }
  1961.  
  1962.      inline void * CL_newD(const Employee * E)
  1963.      {
  1964.           Employee * D = new Employee(0);
  1965.           if (!D) return 0;
  1966.           D->name = (E->name? strdup(E->name) : 0);
  1967.           D->salary = E->salary;
  1968.           return D;
  1969.      }
  1970.  
  1971.      inline void CL_deleteD(Employee * E)
  1972.      { delete E->name; E->name = 0; delete E; }
  1973.  
  1974.      inline ostream& CL_putD(ostream& os, Employee * E)
  1975.      {
  1976.           return os << &((*E).name) << endm
  1977.                << (*E).salary << endm;
  1978.      }
  1979.  
  1980.      inline istream& CL_getD(istream& is, Employee *& E)
  1981.      {
  1982.           if (E) delete E;
  1983.           if ((E = new Employee(0)) != (Employee*)0)
  1984.                is >> &((*E).name) >> nextm
  1985.                     >> (*E).salary >> nextm;
  1986.           return is;
  1987.      }
  1988.  
  1989.      #if defined(CL_NO_TEMPLATES)
  1990.           #define   ITEM      Employee
  1991.           #define CL_ALL_DEF
  1992.           //   #define   CL_CMP_DEF
  1993.           //   #define   CL_ASSIGN_DEF
  1994.           //   #define   CL_NEW_DEF
  1995.           //   #define   CL_DELETE_DEF
  1996.           //   #define   CL_STRM_INSERT_DEF
  1997.           //   #define   CL_STRM_EXTRACT_DEF
  1998.           #define   CL        CL_Employee
  1999.           #include "cl.hf"
  2000.      #else
  2001.           CL_ALL_DEF(Employee)
  2002.           //   CL_CMP_DEF(Employee)
  2003.           #define   CL_Employee       CL<Employee>
  2004.      #endif
  2005.  
  2006.      #define EmployeeFile "employs.tmp"
  2007.  
  2008.      main()
  2009.      {
  2010.           CL_Employee cE(CL_ANDS);
  2011.           cE.ins(new Employee("Doe, John",1000));
  2012.           Employee E("Small, John",100);
  2013.           cE.insQNew(&E);
  2014.           cE.push(new Employee("Morgan, Maria",10000));
  2015.           cE.save(EmployeeFile);
  2016.           cE.allClr();
  2017.           cE.load(EmployeeFile);
  2018.           cE.sort();
  2019.           cout << "\nEmployees in alphabetical order: "
  2020.                << endl;
  2021.           while (cE.nextAsg(&E))
  2022.                cout << E << endl;
  2023.           return 0;
  2024.      }
  2025.  
  2026.  
  2027. Instead of using the macro:        we defined the function: and
  2028. macro:
  2029.  
  2030.  
  2031.      CL_EQ_GT_OPS        CL_cmpD()      CL_CMP_DEF
  2032.      CL_ASSIGN_OP        CL_assignD()   CL_ASSIGN_DEF
  2033.      CL_COPYINIT         CL_newD()      CL_NEW_DEF
  2034.                          CL_deleteD()   CL_DELETE_DEF
  2035.      CL_STRM_OPS         CL_putD()      CL_STRM_INSERT_DEF
  2036.                          CL_getD()      CL_STRM_EXTRACT_DEF
  2037.  
  2038.  
  2039. Notice that CL_cmpD() isn't inlined since we need a proper compare
  2040. function to point to.  The rest are inlined since they will only be
  2041. expanded one time in the overridden virtual functions of the generated
  2042. container wrapper.  CL_deleteD() isn't really necessary since the
  2043. Employee structure already has a destructor.  Notice that these overriding
  2044. mediator function definitions have to appear before the template
  2045. generation!  The CL_ALL_DEF macro is equivalent to all of the
  2046. commented out CL_???_DEF macros which allow you when used
  2047. individually to fine tune a generated wrapper.  The template version
  2048. (versus the form) only requires the use of the CL_CMP_DEF() macro. 
  2049. Again, you can mix and match any of the approaches outlined in this
  2050. chapter.
  2051.  
  2052.  
  2053.  
  2054. Summary
  2055.  
  2056.  
  2057.  
  2058. Typeless containers used by themselves perform no type checking and
  2059. thus can not be made to persist since nothing is known about the data
  2060. bound within.    Likewise a typeless container's "Asg" and "New"
  2061. methods are non functional.  But typeless containers are nevertheless
  2062. useful for holding heterogeneous data types in quick and dirty
  2063. implementations.
  2064.  
  2065. Templates are used to create containers with strong type checking.  If
  2066. your compiler doesn't yet support templates, "form templates" can be
  2067. used in a manner similar to templates.  To gain additional container
  2068. functionality, i.e. default compare function and meaningful "Asg",
  2069. "New", and save() methods you must signal the template or form via the
  2070. configuration macros or mediator functions to generate the appropriate
  2071. wrapper.  Deficiencies in you data types can be accommodated many
  2072. times without deriving new classes by simply invoking special bytewise
  2073. macros or manually overriding mediator functions.
  2074.  
  2075. Strongly typed check containers are used typically to bind homogeneous
  2076. data.  However, heterogeneous data residing in a polymorphic cluster can
  2077. be bound by strongly type checking for a public polymorphic root class. 
  2078. To learn how to add additional functionality to containers of polymorphic
  2079. clusters, see the chapter on polymorphic nodes.
  2080.  
  2081.                                                                   Chapter 4
  2082.  
  2083.  
  2084.                                                     Protected Scope Virtual
  2085. Functions
  2086.  
  2087.  
  2088. In the last chapter on strong type checking you saw the ability of
  2089. Container Lite templates and forms to adapt a container to various data
  2090. types via mediator functions.  These inline mediators are automatically
  2091. generated by the template or form process unless you directly defined
  2092. them yourself.  It is also possible, however, to configure a container for
  2093. any data type by overriding Container Lite's protected scope virtual
  2094. functions in a derived class which is exactly what the template or form
  2095. does with the mediators.
  2096.  
  2097. Let's rework examp303.cpp this time without mediators or templates,
  2098. deriving our own class directly.  The CL_Employee class was created by
  2099. copying the CL class declaration from cl.hf intact and globally replacing
  2100. "CL " with CL_Employee and ITEM with Employee.  Let's look at it
  2101. piece by piece interspersed with my explanation of what's going on. 
  2102. (Source is found in examp401.cpp).
  2103.  
  2104.  
  2105.   class CL_Employee : cl {
  2106.   protected:
  2107.      CL_Employee  (defaultConstructor) : cl(defaultConstruct) {}
  2108.      void     assign(const CL_Employee& b)  { cl::assign(b); }
  2109.      cl::     destruct;
  2110.  
  2111.  
  2112. The default constructor is used by the stream extraction mechanism for
  2113. containers.  It really isn't a default constructor since it has an unnamed
  2114. parameter of type "defaultConstructor."  But this prevents any ambiguity
  2115. that may arise in a user derived class that supplies it's own real default
  2116. constructor.  The assign() function is called by the CL_Employee's copy
  2117. initializer and overloaded assignment operator.  It in turn calls cl::assign. 
  2118. The importance of this arrangement may not be apparent to you just yet. 
  2119. Likewise the destruct() function is called by the CL_Employee's
  2120. destructor.  Destruct() either deletes any remaining nodes in the container
  2121. if the CL_DEL flag is set or simply releases them otherwise.  Nothing
  2122. has been edited thus far.
  2123.  
  2124. Now we come to the important stuff!
  2125.  
  2126.  
  2127.      virtual  voidCmP cmPD(voidCmP cmP)
  2128.      { return (cmP? cmP : CLcmPcast(Employee::cmp,void)); }
  2129.  
  2130.      virtual  void * assignD(void * D, const void * S)
  2131.      {
  2132.           delete ((Employee *)D)->name;
  2133.           ((Employee *)D)->name = (((Employee*)S)->name?
  2134.                strdup(((Employee *)S)->name) : 0);
  2135.           ((Employee *)D)->salary = ((Employee*)S)->salary;
  2136.           return D;
  2137.      }
  2138.  
  2139.      virtual  void * newD(const void * D)
  2140.      {
  2141.           Employee * E = new Employee(0);
  2142.           if (!E) return 0;
  2143.           E->name = (((Employee *)D)->name?
  2144.                strdup(((Employee *)D)->name) : 0);
  2145.           E->salary = ((Employee *)D)->salary;
  2146.           return E;
  2147.      }
  2148.  
  2149.      virtual  void   deleteD(void * D)
  2150.      {
  2151.           delete ((Employee *)D)->name;
  2152.           ((Employee *)D)->name = (char *)0;
  2153.           delete (Employee *)D;
  2154.      }
  2155.  
  2156.  
  2157. The cmPD() function is coded to return the compare function pointer it is
  2158. passed or a pointer to a default compare function if the passed in compare
  2159. function pointer is NULL.  The CLcmPcast() macro (defined in cl.h) type
  2160. casts the cmp function pointer to one that takes void pointer parameters. 
  2161. In the original cl class everything is a pointer to void until we wrap it in a
  2162. template or form generated class or code it ourselves as we are doing
  2163. here.  Recall those examples in earlier chapters which performed sorts
  2164. without a compare function being assigned.  They were able to because
  2165. the template or form generated an overloaded cmPD() that returned a
  2166. default compare function that was in turn generated using the data type's
  2167. implicit or explicit operator==() const and operator>() const functions.
  2168.  
  2169. The assignD() function in our case performs the assignment of one
  2170. Employee to the other.  Container Lite methods like nextAsg() or
  2171. popAsg() are allowed to call assignD if the CL_ASG flag is set. 
  2172. Normally templates and forms override assignD() expanding an inline
  2173. mediator that calls your data type's overloaded assignment operator. 
  2174. Recall that you did this by defining your own mediator or (defining)
  2175. invoking CL_ASSIGN_OP macro before the (form) template instance was
  2176. generated.  If you want to inhibit the "Asg" methods simply overload
  2177. assignD() to always return NULL.  Then regardless of the CL_ASG flag
  2178. setting, "Asg" methods are inhibited.
  2179.  
  2180. The newD() function is called by Container Lite methods like insQNew(),
  2181. but only if the CL_NEW flag is set!  If newD() returns NULL the calling
  2182. method likewise returns failure.
  2183.  
  2184. The deleteD() function is straight forward and is overloaded to call the
  2185. correct destructor for the node's data type.  The deletion of the name
  2186. field isn't really necessary since the Employee's destructor will delete it
  2187. for you.
  2188.  
  2189.  
  2190.      virtual  int    attachD(void * D)
  2191.                { return 1; }
  2192.      virtual  void   detachD(void * D)
  2193.                { return; }
  2194.  
  2195.  
  2196. Actually attachD() and detachD() are used in more sophisticated situations
  2197. where nodes may be attached to more than one container at the same time
  2198. and need to be signaled to whom or how many they are attached to. 
  2199. AttachD() must return true (non zero) before a node is allowed to be
  2200. bound within a container.  This allows your nodes to reject binding on the
  2201. fly.  Your node has nothing to say about becoming detached however.
  2202.  
  2203.  
  2204.      virtual int putD(ostream& os, void * D)
  2205.      {
  2206.           return ((os << &(((Employee *)D)->name) << endm
  2207.                << ((Employee *)D)->salary << endm)? 1 : 0);
  2208.      }
  2209.  
  2210.      virtual void * getD(istream& is)
  2211.      {
  2212.           Employee * E = new Employee(0);
  2213.           if (E)
  2214.                is >> &(E->name) >> nextm
  2215.                     >> E->salary >> nextm;
  2216.           return E;
  2217.      }
  2218.  
  2219.  
  2220. The putD() and getD() virtual functions are responsible for storing and
  2221. recalling your data from stream.
  2222.  
  2223.  
  2224.      virtual  int    put(ostream& os)
  2225.                          { return cl::put(os); }
  2226.      virtual  int    get(istream& is)
  2227.                          { return cl::get(is); }
  2228.  
  2229.  
  2230. If the CL_Employee derived class had itself introduced new data
  2231. members, these put() and get() functions would be responsible for their
  2232. streaming.  But they would still call cl::put() and cl::get() to do the actual
  2233. streaming of the base class members.  If you decide to write the new data
  2234. members to the stream before calling cl::put() then you must read these
  2235. new members from the stream before calling cl::get()!
  2236.  
  2237. Now let's turn our attention to the CL_Employee copy initializer
  2238. constructor, assignment operator, and destructor.
  2239.  
  2240.  
  2241.      CL_Employee (const CL_Employee& b) : cl(defaultConstruct)
  2242.           { assign(b); }
  2243.      CL_Employee& operator=(CL_Employee& b)
  2244.           { assign(b); return *this; }
  2245.      virtual ~CL_Employee() { destruct(); }
  2246.  
  2247.  
  2248. Notice that the assign() function is called after the cl and CL_Employee
  2249. virtual function tables have been initialized.  If we had relied on the cl
  2250. copy initializer constructor to copy the nodes it would have done so with
  2251. cl::assignD() instead of CL_Employee::assignD().  Likewise, if we let the
  2252. cl destructor destroy the nodes then cl::deleteD() would have been called
  2253. instead of the CL_Employee::deleteD() that we overloaded.   The base
  2254. class is always constructed first and destructed last.  During construction-
  2255. destruction, the virtual functions are always initialized to the class level
  2256. defaults of the currently executing constructor-destructor.  Now you can
  2257. see that having the assign() and destruct() functions provides for easier
  2258. coding of the copy constructor and virtual destructor.
  2259.  
  2260. The main() function of examp401.cpp is identical to examp303.cpp thus
  2261. when you run this example you will see it produce exactly the same
  2262. results as examp303.cpp.
  2263.  
  2264.  
  2265. Summary
  2266.  
  2267.  
  2268. In the previous chapters you were exposed to container methods with
  2269. "Asg", "New", and "Del" suffixes and "del" within their names.  Recall
  2270. that these methods performed on the fly assigning, cloning, and deleting
  2271. of your data types, respectively, but otherwise they worked essentially the
  2272. same as their namesake counterparts.  Likewise you saw sort and search
  2273. operations as well as persistence.  These container methods are able to
  2274. adapt to different types of data via the protected scope virtual function
  2275. hooks that either (form) template generation overrides via inline mediator
  2276. functions and/or modifying macros or you as the programmer override
  2277. directly in your own class derived from a Container Lite base class.
  2278.  
  2279.                                                                   Chapter 5
  2280.  
  2281.  
  2282.                                                           Polymorphic Nodes
  2283.  
  2284. Thus far we have only considered binding homogeneous data.  While
  2285. there is no reason why we can't bind heterogeneous data, it remains
  2286. difficult to provide the full spectrum of container services, i.e. "additional
  2287. functionality", owing to the complexities of having to provide a common
  2288. assignment operator, copy initializer, stream operators, etcetera.  Of
  2289. course if this additional functionality isn't required then a generic type
  2290. checked, template or form generated, wrapper class suffices nicely.
  2291.  
  2292. In order for you to more readily construct polymorphic clusters of
  2293. heterogeneous data the pitem.h file declares two classes, Streamable and
  2294. Mutual, either of which you can use as the polymorphic root for your
  2295. family cluster of classes.  Streamable forms the foundation of any
  2296. persistent cluster with run time typing, compatible assignment, cloning,
  2297. and stream processing of the various cluster members.  Mutual is itself
  2298. derived from Streamable and additionally provides for multiple reference
  2299. arbitration in RAM as well as automatic reference resolution during
  2300. streaming operations.  For example if an object with multiple references
  2301. is saved to stream only one copy is saved.  Upon reloading the multiple
  2302. links are automatically reconstructed.
  2303.  
  2304. You can use either Streamable or Mutual in conjunction with a container
  2305. to form polymorphic tree and graph structures.  Use the pitem.cbk file to
  2306. cookbook your derived classes.  It is fully commented with step by step
  2307. instructions.
  2308.  
  2309. The remainder of this chapter will be concerned with using pitem.cbk to
  2310. build a polymorphic persistent cluster of geometric shapes.  A file entitled
  2311. gconsole.cpp is used in an attempt to provide a device independent
  2312. graphical interface to the examples.  It has been set up to work with
  2313. Borland's BGI and the Microsoft graphical run time library as back ends. 
  2314. It shouldn't prove too difficult to add your own back end to gconsole.cpp
  2315. if a different one is required.  (I don't mean to endorse any platform or
  2316. product.  These were the only two environments available to me as I
  2317. wrote the examples for this chapter.)
  2318.  
  2319. We'll start off with a Shape class that provides X and Y coordinates.  We
  2320. will want to eventually derive Circle, Rectangle, and Segment (a
  2321. container of Shapes itself).  We'll proceed this first time in the standard
  2322. fashion without pitem.cbk as follows.
  2323.  
  2324.  
  2325.      // examp501.cpp -- link with cl.obj and graphics.lib.
  2326.  
  2327.      //#define CL_NO_TEMPLATES
  2328.  
  2329.      #include "cl.h"          // endm, nextm
  2330.      #define BGI_PATHNAME "\\bc4\\bgi"
  2331.      #include "gconsole.cpp"
  2332.  
  2333.      class Shape {
  2334.           int x, y;
  2335.           friend ostream& operator<<(ostream&,Shape&);
  2336.           friend istream& operator>>(istream&,Shape&);
  2337.      public:
  2338.           // default constructor required for stream
  2339.           //   extraction
  2340.           Shape   (const Shape& s)
  2341.                { x = s.x; y = s.y; }
  2342.           Shape   (int x = GETRANDX(),
  2343.                int y = GETRANDY())
  2344.                { this->x = x; this->y = y; }
  2345.           int getx() { return x; }
  2346.           int gety() { return y; }
  2347.           Shape&  setxy(int x = GETRANDX(),
  2348.                int y = GETRANDY())
  2349.                { this->x = x; this->y = y;
  2350.                return *this; }
  2351.           virtual void show(int color = GETRANDCOLOR(),
  2352.                int xxpose = 0, int yxpose = 0,
  2353.                int scale = 1)
  2354.                { PUTPIX(x,y,color); }
  2355.           virtual char * name()
  2356.                { return "shape (pixel)"; }
  2357.           virtual ~Shape() {}
  2358.      };
  2359.  
  2360.      // Shape stream insertion/extraction operators
  2361.      inline ostream& operator<<(ostream& os, Shape& s)
  2362.           { return os << s.x << endm << s.y; }
  2363.      inline istream& operator>>(istream& is, Shape& s)
  2364.           { return is >> s.x >> nextm >> s.y; }
  2365.  
  2366.      #ifdef  CL_NO_TEMPLATES
  2367.           #define   ITEM              Shape
  2368.           #define   CL_CLONE_BYTES
  2369.           #define   CL_STRM_OPS
  2370.           #define   CL            Shapes
  2371.           #include "cl.hf"
  2372.      #else
  2373.           CL_CLONE_BYTES(Shape)
  2374.           CL_STRM_OPS(Shape)
  2375.           #define   Shapes CL<Shape>
  2376.      #endif
  2377.  
  2378.      #define  ShapesFile "shapes.tmp"
  2379.  
  2380.      main()
  2381.      {
  2382.           openGraphics();
  2383.           Shapes sb(CL_NEW | CL_DEL |
  2384.                CL_SAVE,1000);
  2385.           Shape s;
  2386.           while (sb.insQNew(&s.setxy()));
  2387.           sb.save(ShapesFile);
  2388.           sb.allDel();
  2389.           sb.load(ShapesFile);
  2390.           while (++sb)  sb.get()->show();
  2391.           cout << "Press <enter> to exit" << endl;
  2392.           (void) cin.get();
  2393.           closeGraphics();
  2394.           return 0;
  2395.      }
  2396.  
  2397.  
  2398. Plain Shapes appear as pixels.  But how can we add Circle to our
  2399. polymorphic cluster of Shapes?  How are the persistence virtual functions,
  2400. putD() and getD(), going to differentiate between a simple Shape and a
  2401. Circle?  We could devise a way for the Shape to have a virtual put().  But
  2402. how do you code a virtual get() for an object that isn't yet got!
  2403.  
  2404. These problems have already been solved for you as will be demonstrated
  2405. in the next two examples.  In the first of these next two examples we start
  2406. over again with Shape derived from Streamable.  Remember this isn't as
  2407. difficult as it may at first appear since this new Shape was coded by
  2408. cloning the pitem.cbk file and following the step by step instructions. 
  2409. Essentially pitem.cbk and Streamable does what you would eventually
  2410. discover for yourself needed to be done to make a polymorphic cluster
  2411. persistent.  As you gain more experience be sure to get into the pitem.cpp
  2412. code to see what's going on.  Let's see the remake of examp501 now.
  2413.  
  2414.  
  2415.      // examp502.cpp
  2416.      // compile: bcc -DCL_NEW_EXCEPTION_MASK
  2417.      //   examp502 cl pitem graphics.lib
  2418.  
  2419.      #include "pitem.h"
  2420.      #define BGI_PATHNAME "\\bc4\\bgi"
  2421.      #include "gconsole.cpp"
  2422.  
  2423.      #define ID_Shape 1U
  2424.  
  2425.      class Shape : public Streamable  {
  2426.  
  2427.           int x, y;
  2428.  
  2429.           void init(
  2430.                // Shape level initializers with
  2431.                // defaults provided for each
  2432.                int x = GETRANDX(),
  2433.                int y = GETRANDY()
  2434.           )
  2435.           { this->x = x; this->y = y; }
  2436.  
  2437.      protected:
  2438.  
  2439.           void assign(const Shape& s)
  2440.                { Streamable::assign
  2441.                (*(const Streamable *)&s);
  2442.                 x = s.x; y = s.y; }
  2443.           Shape(defaultConstructor)
  2444.                : Streamable(defaultConstruct)
  2445.                { init(); }
  2446.           virtual int put(ostream& os);
  2447.           int  get(istream& is);
  2448.           static    StreamablE extract(istream& is);
  2449.  
  2450.      public:
  2451.  
  2452.           static  int register_Class()
  2453.                { return clasSv.regClass
  2454.                (Shape::extract,ID_Shape); }
  2455.  
  2456.           Shape(const Shape& s) : Streamable(defaultConstruct)
  2457.                { init(); assign(s); }
  2458.           Shape(int x = GETRANDX(),
  2459.                int y = GETRANDY())
  2460.                : Streamable()
  2461.                { init(x,y); }
  2462.           virtual   int operator=(const Streamable& s);
  2463.           virtual   StreamablE clone() const
  2464.                { return (StreamablE) new Shape(*this); }
  2465.           virtual   unsigned ID() const
  2466.                { return ID_Shape; }
  2467.           int getx() { return x; }
  2468.           int gety() { return y; }
  2469.           Shape& setxy(int x = GETRANDX(),
  2470.                int y = GETRANDY())
  2471.                { this->x = x; this->y = y;
  2472.                return *this;}
  2473.           virtual void show(int color = GETRANDCOLOR(),
  2474.                int xxpose = 0, int yxpose = 0,
  2475.                int scale = 1)
  2476.                { PUTPIX(x,y,color); }
  2477.           virtual char * name()
  2478.                { return "shape (pixel)"; }
  2479.           virtual   ~Shape()  {}
  2480.  
  2481.      };   /*  class Shape  */
  2482.  
  2483.      int Shape::put(ostream& os)
  2484.      {
  2485.      //   if (Streamable::put(os))  {
  2486.                os << x << endm << y << endm;
  2487.                if (os)
  2488.                     return 1;  // success
  2489.      //   }
  2490.           return 0;
  2491.      }
  2492.  
  2493.      int Shape::get(istream& is)
  2494.      {
  2495.      //   if (Streamable::get(is))  {
  2496.                is >> x >> nextm >> y >> nextm;
  2497.                if (is)
  2498.                     return 1;
  2499.      //   }
  2500.           return 0;
  2501.      }
  2502.  
  2503.      StreamablE Shape::extract(istream& is)
  2504.      {
  2505.           Shape* S;
  2506.           S = new Shape(defaultConstruct);
  2507.           if (S) if (S->get(is))
  2508.                return (StreamablE) S;
  2509.           else
  2510.                delete S;
  2511.           return  StreamablE0;
  2512.      }
  2513.  
  2514.      int Shape::operator=(const Streamable& s)
  2515.      {
  2516.           if (this->ID() == s.ID())  {
  2517.                assign(*(const Shape *)&s);
  2518.                return 1;
  2519.           }
  2520.           return 0;
  2521.      }
  2522.  
  2523.      #if defined(CL_NO_TEMPLATES)
  2524.           #define CL_ALL_DEF
  2525.           CL_PITEM(Shape,Streamable)
  2526.           #define ITEM Shape
  2527.           #define CL Shapes
  2528.           #include "cl.hf"
  2529.      #else
  2530.           CL_PITEM(Shape,Streamable)
  2531.           #define Shapes CL<Shape>
  2532.      #endif
  2533.  
  2534.  
  2535.      #define  ShapesFile "shapes.tmp"
  2536.  
  2537.      main()
  2538.      {
  2539.           openGraphics();
  2540.           Shapes sb(CL_NEW | CL_DEL |
  2541.                CL_SAVE,1000);
  2542.           Shape s;
  2543.           while (sb.insQNew(&s.setxy()));
  2544.           Register_Class(Shape);
  2545.           sb.save(ShapesFile);
  2546.           sb.allDel();
  2547.           sb.load(ShapesFile);
  2548.           while (++sb)  sb.get()->show();
  2549.           cout << "Press <enter> to exit" << endl;
  2550.           (void) cin.get();
  2551.           closeGraphics();
  2552.           return 0;
  2553.      }
  2554.  
  2555.  
  2556. Notice that Shape has a virtual ID() function.  This id is used among
  2557. other things to distinguish a Shape object from let's say a Circle or
  2558. Rectangle.  Shape also has a virtual put() function so that a Circle or
  2559. Rectangle can override it to save any pertinent data particular to its class. 
  2560. Each overridden put() function is only responsible for its class level
  2561. members and calls its base class put() to deal with base class members. 
  2562. Likewise get() only deals with the current level and relies on its base class
  2563. get() to process the base level.  Perhaps the most important aspect of the
  2564. overall design is the extract() function.  Each class derived from a
  2565. Streamable hierarchy must provide a static extract() function that acts as a
  2566. virtual initialize-from-stream constructor.  Each time a Streamable derived
  2567. object is encountered on stream, its unique id is extracted and used to
  2568. look up the object's extract() function that must have been previously
  2569. registered with the Class Registry.  This extractor will default construct
  2570. the appropriate object as required chaining to the base default constructors
  2571. and then start the ball rolling by calling the object's top level get()
  2572. function.  Depending on the order that put() stores data on a stream, get()
  2573. reverses the process loading base class data before or after its own, which
  2574. ever is appropriate.
  2575.  
  2576. Study this example carefully.  Notice how templates and forms handle
  2577. classes derived from Streamable.  The CL_PITEM() macro configures the
  2578. template or form  via mediator functions that handle Streamable or
  2579. Mutual instances, e.g. CL_assignD() invokes the virtual function int
  2580. operator=() and tests to see if the assignment succeeded, while
  2581. CL_newD() invokes the virtual function StreamablE clone().  Notice the
  2582. call to Register_Class() before streaming.  Now we are ready to extend
  2583. our example to incorporate circles and rectangles.  Though both Circle
  2584. and Rectangle are derived from Shape, pitem.cbk can still be used to
  2585. generate their code.  The global replacement of PITEM_STRM_BASE in
  2586. the cloned pitem.cbk file proceeds with Shape this time instead of
  2587. Streamable.  Only the highlights of examp503.cpp are shown here.
  2588.  
  2589.  
  2590.      // examp503.cpp
  2591.      // link with cl.obj, pitem.obj and graphics.lib.
  2592.  
  2593.      ...
  2594.  
  2595.      #define ID_Circle 2U
  2596.  
  2597.      class Circle : public Shape  {
  2598.  
  2599.      protected:
  2600.  
  2601.           int radius;
  2602.  
  2603.      private:
  2604.  
  2605.           void init(int radius = GETRANDY()/8+1)
  2606.                { this->radius = radius; }
  2607.  
  2608.      protected:
  2609.  
  2610.           void assign(const Circle& c)
  2611.                { Shape::assign(*(const Shape *)&c);
  2612.                radius = c.radius; }
  2613.           Circle(defaultConstructor)
  2614.                : Shape(defaultConstruct)
  2615.                { init(); }
  2616.           virtual int put(ostream& os);
  2617.           int  get(istream& is);
  2618.           static    StreamablE extract(istream& is);
  2619.  
  2620.      public:
  2621.  
  2622.           static  int register_Class()
  2623.                { return clasSv.regClass
  2624.                (Circle::extract,ID_Circle); }
  2625.  
  2626.           Circle(const Circle& s) : Shape(defaultConstruct)
  2627.                { init(); assign(s); }
  2628.           Circle(int radius = GETRANDY()/8+1,
  2629.                int x = GETRANDX(),
  2630.                int y = GETRANDY())
  2631.                : Shape(x,y)
  2632.                { init(radius); }
  2633.           virtual   unsigned ID() const
  2634.                { return ID_Circle; }
  2635.           virtual void show(int color, int xxpose,
  2636.                int yxpose, int scale)
  2637.                {
  2638.                     SETCOLOR(color);
  2639.                     CIRCLE(getx()+xxpose,
  2640.                          gety()+yxpose,
  2641.                          (radius*scale));
  2642.                }
  2643.           virtual char * name() { return "circle"; }
  2644.           virtual   ~Circle()  {}
  2645.  
  2646.      };   /*  class Circle  */
  2647.  
  2648.  
  2649.      int Circle::put(ostream& os)
  2650.      {
  2651.           if (Shape::put(os))  {
  2652.                os << radius << endm;
  2653.                if (os)
  2654.                     return 1;  // success
  2655.           }
  2656.           return 0;
  2657.      }
  2658.  
  2659.      int Circle::get(istream& is)
  2660.      {
  2661.           if (Shape::get(is))  {
  2662.                is >> radius >> nextm;
  2663.                if (is)
  2664.                     return 1;
  2665.           }
  2666.           return 0;
  2667.      }
  2668.  
  2669.      StreamablE Circle::extract(istream& is)
  2670.      {
  2671.           Circle* S;
  2672.           S = new Circle(defaultConstruct);
  2673.           if (S) if (S->get(is))
  2674.                return (StreamablE) S;
  2675.           else
  2676.                delete S;
  2677.           return  StreamablE0;
  2678.      }
  2679.  
  2680.      ...
  2681.  
  2682.      //  Shape polymorphic cluster processing
  2683.      //  remains outside compiled cluster library
  2684.      //  to allow cluster extensibility.
  2685.  
  2686.      Shape * Shape::newShape()
  2687.      {
  2688.           int ID = random(ID_Rectangle-ID_Shape+1)
  2689.                + ID_Shape;
  2690.  
  2691.           switch (ID)  {
  2692.             case ID_Shape:     return new Shape();
  2693.             case ID_Circle:    return new Circle();
  2694.             case ID_Rectangle: return new Rectangle();
  2695.      // add new cluster members here:
  2696.             default:           return (Shape *)0;
  2697.           }
  2698.      }
  2699.  
  2700.      void Shape::TallyShowApply(Shape * S, va_list args)
  2701.      {
  2702.           int   xxpose     =  va_arg(args,int);
  2703.           int   yxpose     =  va_arg(args,int);
  2704.           int   scale      =  va_arg(args,int);
  2705.           int * shapes     =  va_arg(args,int *);
  2706.           int * circles    =  va_arg(args,int *);
  2707.           int * rectangles =  va_arg(args,int *);
  2708.      // add new cluster members here:
  2709.           switch (S->ID())  {
  2710.             case ID_Shape:     ++*shapes;    break;
  2711.             case ID_Circle:    ++*circles;   break;
  2712.             case ID_Rectangle: ++*rectangles;break;
  2713.      // and here:
  2714.           }
  2715.           
  2716.           S->show(GETRANDCOLOR(),xxpose,yxpose,scale);
  2717.      }
  2718.  
  2719.      #define  ShapesFile "shapes.tmp"
  2720.  
  2721.      main()
  2722.      {
  2723.           openGraphics();
  2724.           Shapes sb(CL_ANDS,100);
  2725.           Shape *S;
  2726.           while (sb.insQ(S = Shape::newShape()));
  2727.           delete S;
  2728.           Register_Class(Shape);
  2729.           Register_Class(Circle);
  2730.           Register_Class(Rectangle);
  2731.           sb.save(ShapesFile);
  2732.           sb.allDel();
  2733.           sb.load(ShapesFile);
  2734.           int shapes = 0, circles = 0, rectangles = 0;
  2735.           sb.forEach(Shape::TallyShowApply,0,0,1,
  2736.                &shapes,&circles,&rectangles);
  2737.           cout << "Shapes (pixels): " << shapes
  2738.                << "  cirlces: " << circles
  2739.                << "  rectangles: " << rectangles
  2740.                << endl;
  2741.           S = Shape::newShape();
  2742.           unsigned i = sb.tallyAll(S);
  2743.           cout << "A total of " << i << " "
  2744.                << S->name() << "(s)"
  2745.                << " were found"
  2746.                << endl;
  2747.           delete S;
  2748.           cout << "Press <enter> to exit"  << endl;
  2749.           (void) cin.get();
  2750.           closeGraphics();
  2751.           return 0;
  2752.      }
  2753.  
  2754.  
  2755. Okay, we have succeeded with a container of polymorphic Shapes that's
  2756. persistent!  Let's push on to a tree structure, namely adding the Segment
  2757. shape.  A Segment is itself a Shape and a container of Shapes.  Pitem.cbk
  2758. again comes to the rescue, generating sublists.  Excerpts of examp504.cpp
  2759. are shown next.
  2760.  
  2761.  
  2762.      // examp504.cpp
  2763.  
  2764.      ...
  2765.  
  2766.      #define ID_Segment 4U
  2767.  
  2768.      #define SGM_MAXSHAPES 20
  2769.  
  2770.      class Segment : public Shape
  2771.  
  2772.           , public Shapes     // container of Shape
  2773.  
  2774.      {
  2775.  
  2776.           void init() {}
  2777.  
  2778.      protected:
  2779.  
  2780.           void assign(const Segment& i)
  2781.                { Shape::assign(*(const Shape *)&i);
  2782.                Shapes::assign(*(const Shapes *)&i);
  2783. }
  2784.           Segment(defaultConstructor)
  2785.                : Shape(defaultConstruct)
  2786.                , Shapes(defaultConstruct)
  2787.                { init(); }
  2788.           virtual int put(ostream& os);
  2789.           int  get(istream& is);
  2790.           static    StreamablE extract(istream& is);
  2791.  
  2792.      public:
  2793.  
  2794.           static  int register_Class()
  2795.                { return clasSv.regClass
  2796.                (Segment::extract,ID_Segment); }
  2797.  
  2798.           Segment(const Segment& s)
  2799.                : Shape(defaultConstruct)
  2800.                , Shapes(defaultConstruct)
  2801.                { init(); assign(s); }
  2802.           Segment   (int x =  0, int y =  0,
  2803.                unsigned flags = CL_ANDS,
  2804.                unsigned maxNodes = SGM_MAXSHAPES,
  2805.                unsigned limit = CL_LIMIT,
  2806.                unsigned delta = CL_DELTA
  2807.                ) : Shape (x,y)
  2808.                , Shapes (flags,maxNodes,limit,delta)
  2809.                { init(); }
  2810.  
  2811.           virtual unsigned restream();
  2812.  
  2813.           virtual   unsigned ID() const
  2814.                { return ID_Segment; }
  2815.           virtual void show(int color, int xxpose,
  2816.                int yxpose, int scale);
  2817.           virtual char * name() { return "segment"; }
  2818.           virtual   ~Segment()  {}
  2819.  
  2820.      };   /*  class Segment  */
  2821.  
  2822.      int Segment::put(ostream& os)
  2823.      {
  2824.           if (Shape::put(os))
  2825.                return Shapes::put(os);
  2826.           return 0;
  2827.      }
  2828.  
  2829.      int Segment::get(istream& is)
  2830.      {
  2831.           if (Shape::get(is))
  2832.                return Shapes::get(is);
  2833.           return 0;
  2834.      }
  2835.  
  2836.      StreamablE Segment::extract(istream& is)
  2837.      {
  2838.           Segment * S;
  2839.           S = new Segment(defaultConstruct);
  2840.           if (S) if (S->get(is))
  2841.                return (StreamablE) S;
  2842.           else
  2843.                delete S;
  2844.           return  StreamablE0;
  2845.      }
  2846.  
  2847.      unsigned Segment::restream()
  2848.      {
  2849.           unsigned underFlow = 0;
  2850.           for (unsigned i = 0; i < Shapes::Nodes(); i++)
  2851.                underFlow += (atGet(i)->restream());
  2852.           return (underFlow + Shape::restream());
  2853.      }
  2854.  
  2855.      #pragma argsused
  2856.      void Segment::show(int color, int xxpose, int yxpose,
  2857.           int scale)
  2858.      {
  2859.           for (unsigned i = 0; i < Nodes(); i++)
  2860.                atGet(i)->show(color,getx()+xxpose,
  2861.                     gety()+yxpose,scale);
  2862.      }
  2863.  
  2864.      //  Shape polymorphic cluster processing
  2865.      //  remains outside compiled cluster library
  2866.      //  to allow cluster extensibility.
  2867.  
  2868.      Shape * Shape::newSimpleShape(int ID)
  2869.      {
  2870.           switch (ID)  {
  2871.             case ID_Shape:     return new Shape();
  2872.             case ID_Circle:    return new Circle();
  2873.             case ID_Rectangle: return new Rectangle();
  2874.      // add new cluster members here:
  2875.             default:           return (Shape *)0;
  2876.           }
  2877.      }
  2878.  
  2879.      Shape * Shape::newShape()
  2880.      {
  2881.        int ID = random(ID_Segment-ID_Shape+1) + ID_Shape;
  2882.        if (ID == ID_Segment)  {
  2883.          Segment * S = new Segment();
  2884.          if (S)
  2885.            while (S->insQ(Shape::
  2886.                newSimpleShape(random
  2887.                (ID_Rectangle-ID_Shape+1)+ID_Shape)));
  2888.          return S;
  2889.        }
  2890.        return newSimpleShape(ID);
  2891.      }
  2892.  
  2893.      void Shape::TallyShowApply(Shape * S, va_list args)
  2894.      {
  2895.           int   color      =  va_arg(args,int);
  2896.           int   xxpose     =  va_arg(args,int);
  2897.           int   yxpose     =  va_arg(args,int);
  2898.           int   scale      =  va_arg(args,int);
  2899.           int * shapes     =  va_arg(args,int *);
  2900.           int * circles    =  va_arg(args,int *);
  2901.           int * rectangles =  va_arg(args,int *);
  2902.           int * segments   =  va_arg(args,int *);
  2903.      // add new cluster members here:
  2904.           switch (S->ID())  {
  2905.             case ID_Shape:     ++*shapes;    break;
  2906.             case ID_Circle:    ++*circles;   break;
  2907.             case ID_Rectangle: ++*rectangles;break;
  2908.             case ID_Segment:   ++*segments;  break;
  2909.      // and here:
  2910.           }
  2911.           if (S->ID() == ID_Segment)
  2912.                ((Segment *)S)->forEach(Shape::
  2913.                     TallyShowApply,GETRANDCOLOR(),
  2914.                     S->getx(),S->gety(),scale,
  2915.                     shapes,circles,
  2916.                     rectangles,segments);
  2917.           else
  2918.                S->show(color,xxpose,yxpose,scale);
  2919.      }
  2920.  
  2921.      #define  ShapesFile "shapes.txt"
  2922.  
  2923.      main()
  2924.      {
  2925.           openGraphics();
  2926.           Shapes sb(CL_ANDS,20);
  2927.           Shape *S;
  2928.           while (sb.insQ(S = Shape::newShape()));
  2929.           delete S;
  2930.           Register_Class(Shape);
  2931.           Register_Class(Circle);
  2932.           Register_Class(Rectangle);
  2933.           Register_Class(Segment);
  2934.           sb.save(ShapesFile);
  2935.           sb.allDel();
  2936.           sb.load(ShapesFile);
  2937.           int shapes = 0, circles = 0, rectangles = 0;
  2938.           int segments = 0;
  2939.           sb.forEach(Shape::TallyShowApply,
  2940.                GETRANDCOLOR(),0,0,1,
  2941.                &shapes,&circles,
  2942.                &rectangles,&segments);
  2943.           cout << "Shapes (pixels): " << shapes
  2944.                << "  circles: " << circles
  2945.                << "  rectangles: " << rectangles
  2946.                << "  segments: " << segments
  2947.                << endl;
  2948.           S = Shape::newShape();
  2949.           unsigned i = sb.findAll(S);
  2950.           cout << "A total of " << i << " "
  2951.                << S->name() << "(s)"
  2952.                << " were found at the first level"
  2953.                << endl;
  2954.           delete S;
  2955.           cout << "Press <enter> to exit"  << endl;
  2956.           (void) cin.get();
  2957.           closeGraphics();
  2958.           return 0;
  2959.      }
  2960.  
  2961.  
  2962. Besides Shapes (pixels), Circles, and Rectangles, we are able to display
  2963. Segments of Shapes, Circles, and Rectangles.  The TallyShowApply()
  2964. function used in conjunction with the container's forEach() iterator
  2965. provides an excellent example of how to pass variable argument lists to
  2966. multiple sub-levels.
  2967.  
  2968. The power of a real graphics subsystem lies in the ability of graphic
  2969. segments to be multiply referenced while citing different scales and
  2970. transpositions.  The next example is a remake of examp504 rooting Shape
  2971. in a Mutual base instead of a Streamable one.  This leads us to create yet
  2972. an additional new Shape called SegRef for Segment Reference.  I
  2973. generated examp505.cpp by modifying examp504.cpp so that all
  2974. Streamable references were replaced with Mutual.  I could have used
  2975. pitem.cbk as well.  Such is the case with SegRef and PieSlice, yet another
  2976. Shape.   Here is a snippet of examp505.cpp.
  2977.  
  2978.  
  2979.      // examp505.cpp
  2980.  
  2981.      ...
  2982.  
  2983.      #define ID_SegRef 6U
  2984.  
  2985.      class SegRef : public Shape
  2986.  
  2987.      {
  2988.  
  2989.           Segment * S;
  2990.           void discardS();
  2991.           void init(Segment * S = 0);
  2992.  
  2993.      protected:
  2994.  
  2995.           void assign(const SegRef& i);
  2996.           SegRef(defaultConstructor)
  2997.                : Shape(defaultConstruct)
  2998.                { init(); }
  2999.           virtual int put(ostream& os);
  3000.           int  get(istream& is);
  3001.           static    MutuaL extract(istream& is);
  3002.  
  3003.      public:
  3004.  
  3005.           static  int register_Class()
  3006.                { return clasSv.regClass
  3007.                (SegRef::extract,ID_SegRef); }
  3008.  
  3009.           SegRef(const SegRef& s) : Shape(defaultConstruct)
  3010.                { init(); assign(s); }
  3011.           SegRef(Segment * S = 0,
  3012.                int x = GETRANDX(),
  3013.                int y = GETRANDY())
  3014.                : Shape(x,y)
  3015.                { init(S); }
  3016.           virtual unsigned restream();
  3017.           virtual StreamablE clone() const
  3018.                { return (StreamablE)
  3019.                new SegRef(*this); }
  3020.           virtual   unsigned ID() const
  3021.                { return ID_SegRef; }
  3022.           virtual void show(int color = GETRANDCOLOR(),
  3023.                int xxpose = 0, int yxpose = 0,
  3024.                int scale = 1)
  3025.                { if (S) S->show(color,getx()+xxpose,
  3026.                gety()+yxpose,scale); }
  3027.           virtual char * name()
  3028.                { return "segment reference"; }
  3029.           virtual   ~SegRef() { discardS(); }
  3030.  
  3031.      };   /*  class SegRef  */
  3032.  
  3033.      void SegRef::discardS()
  3034.      {
  3035.           if (S)  {
  3036.                S->unlink(this);
  3037.                if (!S->RefCount())
  3038.                     delete S;
  3039.                S = 0;
  3040.           }
  3041.      }
  3042.  
  3043.      void SegRef::init(Segment * S)
  3044.      {
  3045.           this->S = 0;
  3046.           if (S) if (S->link(this))
  3047.                this->S = S;
  3048.      }
  3049.  
  3050.      void SegRef::assign(const SegRef& i)
  3051.      {
  3052.           Shape::assign(*(const Shape *)&i);
  3053.           discardS();
  3054.           init(i.S);
  3055.      }
  3056.  
  3057.      int SegRef::put(ostream& os)
  3058.      {
  3059.           if (Shape::put(os))  {
  3060.                int Sok;
  3061.                os << (Sok = (S != 0)) << endm;
  3062.                if (Sok)
  3063.                     os << *(MutuaL)S << endm;
  3064.                if (os)
  3065.                     return 1;
  3066.           }
  3067.           return 0;
  3068.      }
  3069.  
  3070.      int SegRef::get(istream& is)
  3071.      {
  3072.           if (Shape::get(is))  {
  3073.                int Sok;
  3074.                is >> Sok >> nextm;
  3075.                if (Sok)  {
  3076.                     MutuaL M;
  3077.                     is >> M >> nextm;
  3078.                     S = (Segment *)M;
  3079.                     if (S)
  3080.                          S->link(this);
  3081.                }
  3082.                if (is)
  3083.                     return 1;
  3084.           }
  3085.           return 0;
  3086.      }
  3087.  
  3088.      MutuaL SegRef::extract(istream& is)
  3089.      {
  3090.           SegRef * S;
  3091.  
  3092.           S = new SegRef(defaultConstruct);
  3093.           if (S) if (S->get(is))
  3094.                return (MutuaL) S;
  3095.           else
  3096.                delete S;
  3097.           return  MutuaL0;
  3098.      }
  3099.  
  3100.      unsigned SegRef::restream()
  3101.      {
  3102.           unsigned underFlow = 0U;
  3103.           if (S) if (S->StreamPos())
  3104.                underFlow = S->restream();
  3105.           return (underFlow + Shape::restream());
  3106.      }
  3107.  
  3108.      #define  ShapesFile "shapes.txt"
  3109.  
  3110.      main()
  3111.      {
  3112.           openGraphics();
  3113.           Shapes sb(CL_ANDS,5);
  3114.           Segment * S = new Segment();
  3115.           if (!S)  {
  3116.                closeGraphics();
  3117.                return 1;
  3118.           }
  3119.           *S
  3120.                << new PieSlice(60,300,
  3121.                     GETMAXY()/16,0,0)
  3122.                << new Circle(GETMAXY()/8,0,0)
  3123.                << new Rectangle(GETMAXX()/8,
  3124.                          GETMAXY()/8,0,0);
  3125.           SegRef * SR = new SegRef(S);
  3126.           if (!SR)  {
  3127.                delete S;
  3128.                closeGraphics();
  3129.                return 1;
  3130.           }
  3131.           sb << SR;
  3132.           while (sb.insQ(SR = new SegRef(S)));
  3133.           delete SR;
  3134.           Register_Class(Shape);
  3135.           Register_Class(Circle);
  3136.           Register_Class(Rectangle);
  3137.           Register_Class(PieSlice);
  3138.           Register_Class(Segment);
  3139.           Register_Class(SegRef);
  3140.           sb.save(ShapesFile);
  3141.           for (sb.setCurNode(); ++sb; sb.get()->restream());
  3142.           sb.allDel();
  3143.           sb.load(ShapesFile);
  3144.           Restream();
  3145.           while (++sb)
  3146.                sb.get()->show(GETRANDCOLOR());
  3147.           cout << "Press <enter> to exit"  << endl;
  3148.           (void) cin.get();
  3149.           closeGraphics();
  3150.           return 0;
  3151.      }
  3152.  
  3153.  
  3154. You'll notice that the example code didn't have to worry about multiple
  3155. reference resolution or reconstruction.  It was all done transparently
  3156. behind the scenes in pitem.cpp.  But I did have to insure that SegReg
  3157. signals its Segment each time it was linked or unlinked from its SegRef
  3158. (see discardS(), init(), and get() above).  Segment's inherited Shapes
  3159. container performs un/linking on all its nodes (Shapes) automatically.  If
  3160. you want to save a Mutual network again on stream you must call
  3161. restream() for each Mutual derived object.  The for loop immediately
  3162. following sb.save() demonstrates this though it isn't necessary in our
  3163. example since we don't save the shapes more than once.  The restream()
  3164. function zeros the streamPos and streamCount members of a Mutual
  3165. derived object.  If pitem.cpp was compiled with MUTUAL_DEBUG
  3166. defined then any discrepancies between refCount and the number of times
  3167. the object was "streamed", i.e. streamCount, is reported on cerr.  In any
  3168. case this underflow tally is return by restream().  Take a moment to
  3169. review SegRef::restream() since it is slightly different than the pitem.cbk
  3170. format suggests.  Remember that more than one SegRef object can be
  3171. linked to a Segment object.  Once restream() is called for the Segment its
  3172. streamPos is zeroed.  SegRef::restream() only calls Segment::restream() if
  3173. it hasn't been called already.  The call to Restream() in main() is a
  3174. slightly different matter.  It is done after loading to insure the Mutual
  3175. holding pen data base is cleared.  This Restream() is a macro that calls
  3176. MutualHoldingPen::restream() for you.  If pitem.cpp is compiled with
  3177. MUTUAL_DEBUG defined then calling Restream() reports any multiple
  3178. reference reconstruction discrepancies on cerr.
  3179.  
  3180.  
  3181. Summary
  3182.  
  3183.  
  3184.  
  3185. You'll most likely never use the Container Lite tool at this level of
  3186. complexity, but it's there if you ever need it.  Remember pitem.cpp
  3187. processes polymorphic persistent nodes for you.  Use the Streamable class
  3188. for basic polymorphic clusters and Mutual if you need multiple reference
  3189. handling.  Use pitem.cbk as a boiler plate template to generate your
  3190. derived classes by following the step by step instructions in its comments.
  3191.  
  3192.                                                                   Chapter 6
  3193.  
  3194.  
  3195.                                                                   Reference
  3196.  
  3197.  
  3198.  
  3199. Type-less Container
  3200.  
  3201.  
  3202.  
  3203. A container is an elastic array of void pointers having the added behaviors
  3204. of stack, queue, deque, list, etcetera.  To achieve strong type checking
  3205. use templates or forms (see chapter 3).  Strongly typed prototypes read
  3206. the same as shown here for a typeless container except that void pointers,
  3207. i.e. void *, are replaced with pointers to your specified data types.
  3208.  
  3209. Internals
  3210.  
  3211. Functions, private:
  3212.  
  3213.      void init(unsigned flags = 0U,
  3214.                unsigned maxNodes = 0U,
  3215.                unsigned limit = 0U,
  3216.                unsigned delta = 0U);
  3217.  
  3218.      The init() initializer is called by all the container constructors thus
  3219.      guaranteeing consistency.
  3220.  
  3221. Members, protected:
  3222.  
  3223.           unsigned  lowLimit,  lowThreshold, first;
  3224.           void *V   linkS;
  3225.           unsigned  limit,     delta,    nodes;
  3226.           unsigned  maxNodes,  curNode,  flags;
  3227.           CLcmP     cmP;
  3228.  
  3229.      The value of "limit" is the current size of the array pointed to by
  3230.      "linkS."  The value of "delta" is how much the array will grow
  3231.      upon overflow.  "Nodes" is the number of occupied cells in the
  3232.      array.  "MaxNodes" is the upper bound on the growth of limit. 
  3233.      "LowTheshold" is the trigger for contraction.  When the number
  3234.      of logically occupied cells in the container becomes equal to
  3235.      "lowThreshold" the physical array is compacted to a new limit of
  3236.      "lowLimit."  "First" maps the container's logical cell zero into a
  3237.      starting cell position in the physical array pointed to by "linkS." 
  3238.      "CurNode" is the current node associated with the container's list
  3239.      behavior.  When no nodes are present "linkS" remains NULL.
  3240.  
  3241.      "Flags" has the following bit definitions.
  3242.  
  3243.           #define CL_SORTED        0x0001U
  3244.           #define CL_BIND_ONLY     0x0000U
  3245.           #define CL_ASG           0x0002U
  3246.           #define CL_READ          0x0004U
  3247.           #define CL_READ_DEL      0x0008U
  3248.           #define CL_NEW           0x0010U
  3249.           #define CL_NEWED         0x0020U
  3250.           #define CL_DEL           0x0040U
  3251.           #define CL_SAVE          0x0080U
  3252.           #define CL_ALL_FLAGS     0x00FFU
  3253.           #define CL_ANDS          (CL_ASG|CL_NEW|\
  3254.                                    CL_DEL|CL_SAVE)
  3255.  
  3256.      The CL_SORTED flag indicates whether the container is still
  3257.      sorted since the last sort operation.  CL_BIND_ONLY is the
  3258.      absence of CL_ASG, CL_NEW, CL_DEL, and CL_SAVE flags. 
  3259.      CL_ASG enables methods ending with the "Asg" suffix.  The
  3260.      CL_READ flag is set during an "Asg" operation that copies data
  3261.      from a node to an external location.  An overloaded assignD()
  3262.      virtual function or the CL_assignD() mediator function can test
  3263.      this flag if required.  Likewise the CL_READ_DEL flag is set
  3264.      instead of the CL_READ flag during a "DelAsg" operation. 
  3265.      CL_NEW enables the methods ending with the "New" suffix. 
  3266.      CL_NEWED is set if ever newD() is invoked by a "New" method. 
  3267.      Use it to detect memory leaks, e.g. a non self deleting container. 
  3268.      CL_DEL enables the methods with "Del" or "del" in their names. 
  3269.      It also causes the destructor to call allDel() instead of allRmv(). 
  3270.      CL_SAVE enables save() and the overloaded stream insertion
  3271.      operator.
  3272.  
  3273.      The cmP variable holds the current compare function pointer.
  3274.  
  3275. Functions, protected:
  3276.  
  3277.      cl  (defaultConstructor)  { init(); }
  3278.  
  3279.           This pseudo default constructor provides a mechanism for
  3280.           stream loader functions to construct an object's hierarchy
  3281.           without initializing it.  The parameter is a dummy to
  3282.           present a style for latter extensibility that will insure that an
  3283.           unambiguous default constructor is available.
  3284.  
  3285.      void  assign(const cl& b);
  3286.  
  3287.           Assigns the state (maxNodes, flags, cmP, etc.) of container
  3288.           b to the current container after disposing of any nodes. 
  3289.           The nodes of container b are cloned and these clones are
  3290.           bound within the current container.  Assign() is meant to be
  3291.           called by a derived class' assign().  In this manner a copy
  3292.           initializer constructor of the derived class calls its container
  3293.           base class default constructor and calls its own assign()
  3294.           which in turn calls cl::assign().  By taking this approach we
  3295.           insure that DerivedFromCL:: assignD() and
  3296.           DerivedFromCL:: attachD() are called in the copy
  3297.           initializer constructor instead of cl:: assignD() and cl::
  3298.           attachD() which would have been the case if the derived
  3299.           class called a container copy initializer constructor.  See the
  3300.           copy constructor definition in cl.hpt/hpf for proper use. 
  3301.           Also see notes on destruct() to further your understanding
  3302.           of this phenomenon.
  3303.  
  3304.      void  destruct();
  3305.  
  3306.           You must override the container destructor in any
  3307.           descendant that overrides deleteD() or detachD(), i.e.
  3308.  
  3309.                virtual ~DerivedFromCL()
  3310.                          { cl::destruct(); }
  3311.  
  3312.           This is because the base destructors are called after virtual
  3313.           function tables are reset to their default values for the base
  3314.           level.  This means that cl::~cl() calls cl::detachD() and
  3315.           perhaps cl::deleteD() instead of the intended
  3316.           DerivedFromCL:: detachD() or DerivedFromCL::
  3317.           deleteD().  By performing the actual destruction in the
  3318.           destruct() function the overridden destructor can be simply
  3319.           coded inline as shown.
  3320.  
  3321.      virtual voidCmP cmPD(voidCmP cmP)  { return cmP; }
  3322.  
  3323.           This function is designed to return cmP or a default
  3324.           compare function pointer if cmP is NULL.  The template
  3325.           and form instances override cmPD() in such a manner. 
  3326.           You can also overload it yourself (see examp207.cpp).  The
  3327.           mediator function CL_cmpD() can also be user defined to
  3328.           customize template generation of an overloaded version of
  3329.           this function for data types lacking relational operators (see
  3330.           examp303.cpp).
  3331.  
  3332.      virtual void * assignD(void * D, const void * S)
  3333.                { return (void *) 0; }
  3334.  
  3335.           Called by atPutAsg(), atGetAsg(), popAsg(), etcetera, to
  3336.           copy data between a node and an external object.  Template
  3337.           and form instances override assignD() redefining it to call
  3338.           the operator=() function for your data type.  It is
  3339.           guaranteed that assignD() is never called unless the
  3340.           CL_ASG flag is set and the parameters are not NULL! 
  3341.           The first parameter is the destination while the second is
  3342.           the source.  If assignD() returns NULL the calling method
  3343.           gracefully fails leaving the container in an unaltered state. 
  3344.           If assignD() is being called from a reading method such as
  3345.           atGetAsg() versus atPutAsg(), the CL_READ flag is set.  If
  3346.           assignD() is being called from a reading deletion method
  3347.           such as atDelAsg() then CL_READ_DEL flag is set
  3348.           instead.  This allows your overloaded version to known the
  3349.           exact conditions of the assignment should that become
  3350.           necessary to your application.  For example, if
  3351.           CL_READ_DEL is set you may want to copy just the
  3352.           pointers to suballocated memory from the node about to be
  3353.           deleted instead of making a duplicate and then turn around
  3354.           and let the container destroy the originals.
  3355.  
  3356.           A quick look at cl.h/hf reveals an overloaded assignD() that
  3357.           calls the mediator function CL_assignD().  CL_assignD() is
  3358.           automatically (form) template inline generated unless
  3359.           manually overridden (see examp303.cpp).
  3360.  
  3361.      virtual void * newD(const void *)  { return (void *) 0; }
  3362.  
  3363.           Called by atInsNew(), pushNew(), atPutNew(), etcetera, to
  3364.           clone the data given as the parameter.  Template and form
  3365.           instances override newD() redefining it to call the copy
  3366.           initializer constructor for your data type.  It is guaranteed
  3367.           that newD() is never called unless the CL_NEW flag is set
  3368.           and the parameter is not NULL!  If newD() returns NULL
  3369.           the calling method gracefully fails leaving the container in
  3370.           an unaltered state.
  3371.  
  3372.           A quick look at cl.h/hf reveals an overloaded newD() that
  3373.           calls the mediator function CL_newD().  CL_newD() is
  3374.           automatically (form) template inline generated unless
  3375.           manually overridden (see examp303.cpp).
  3376.  
  3377.      virtual void deleteD(void * D)  { delete (void*) D; }
  3378.  
  3379.           Called by atDel(), allDel(), popDel(), del(), etcetera, to
  3380.           delete container nodes as they are removed from the
  3381.           container.  Template and form instances override deleteD()
  3382.           redefining it to call the appropriate destructor for your data
  3383.           type via the automatically inline generated CL_deleteD(). 
  3384.           It is guaranteed that deleteD() is never called unless the
  3385.           CL_DEL flag is set and the D parameter is not NULL.  As
  3386.           a side note, containers of Streamable/Mutual nodes carry
  3387.           out the deletion of nodes only when their refCount's are
  3388.           zero.
  3389.  
  3390.      virtual int  attachD(void *)  { return 1; }
  3391.  
  3392.           Called by any container method that attempts to bind data,
  3393.           e.g. atIns(), push(), etcetera.  This allows the data to
  3394.           become aware of the binding process by overriding
  3395.           attachD().  The void * parameter is a pointer to the data
  3396.           about to be bound and is guaranteed never to be NULL!  If
  3397.           and only if the data refuses to attach itself to the container
  3398.           should zero be returned.  Any overriding function should
  3399.           only link itself to the container and should not use the
  3400.           implicit "this" pointer to access the container within the
  3401.           overriding function.  This restriction applies because
  3402.           container data may be in a transition state when attachD() is
  3403.           called.  It is however permissible to store the "this" pointer
  3404.           within the data being bound for later use in accessing
  3405.           container members.  Template and form instances do not
  3406.           override this function except for Streamable/Mutual derived
  3407.           data types which make use of this function allowing a
  3408.           container to signal node linkage.
  3409.  
  3410.      virtual void detachD(void *)  { return; }
  3411.  
  3412.           detachD() is called by any container method that attempts
  3413.           to unbind data, e.g. atDel(), pop(), etcetera.  The void *
  3414.           parameter is never NULL!  Once called, the node must
  3415.           consider itself detached from the container!  Template and
  3416.           form instances do not override this function except for
  3417.           Streamable/Mutual derived data types which make use of
  3418.           this function allowing a container to signal released nodes.
  3419.  
  3420.      virtual int putD(ostream&, void *)  { return 0; }
  3421.  
  3422.           putD() is called for each node after the container's header
  3423.           is stored on the stream.  The void * parameter is
  3424.           guaranteed never to be NULL.  Template and form
  3425.           instances override this function redefining it to invoke the
  3426.           overloaded stream insertion operator for your data type.  If
  3427.           your overridden putD() is successful it should return a non
  3428.           zero value.
  3429.  
  3430.      virtual void * getD(istream&) { return (void*)0; }
  3431.  
  3432.           getD() is called for each node on a stream after the
  3433.           container's header is loaded.  The container binds what
  3434.           getD returns.  Template and form instances override this
  3435.           function redefining it to allocate a new variable for your
  3436.           data type with its default constructor, on which it then
  3437.           invokes the overloaded stream extraction operator.
  3438.  
  3439.      virtual int put(ostream& os);
  3440.  
  3441.           Called by the save() function and the container's stream
  3442.           insertion operator defined by the template and form
  3443.           instances but only if the CL_SAVE flag is set.  The
  3444.           container's header is saved first followed by its nodes.  The
  3445.           nodes are saved via putD().  Template and form instances
  3446.           do not override this put() function.  Instead they override
  3447.           putD().  The purpose of overriding the put() function would
  3448.           be to store additional header information declared in a class
  3449.           derived from the container.
  3450.  
  3451.      virtual int get(istream& is);
  3452.  
  3453.           Called by the load() function and the container's stream
  3454.           extraction operator defined by the template and form
  3455.           instances.  The container's header data is loaded first
  3456.           followed by its nodes.  The nodes are retrieved via getD(). 
  3457.           Template and form instances do not override this get()
  3458.           function.  Instead they override getD().  The purpose of
  3459.           overriding the get() function would be to load additional
  3460.           header information declared in a class derived from the
  3461.           container.
  3462.  
  3463.      int load(const char * filename);
  3464.  
  3465.           Any nodes in the container are first cleared then the file
  3466.           stream is opened and get() is called to load the saved
  3467.           container and its nodes.  If successful a non zero value is
  3468.           returned.  The complete state of the saved container is
  3469.           restored, i.e. maxNodes, curNode, flags, etc. and cmP (if
  3470.           user defined, the function must be registered prior to
  3471.           saving, see Register_CmP() under FunctionRegistry). 
  3472.           Template and form instances promote this function to
  3473.           public scope because only when the data type is known can
  3474.           the container persist, i.e. getD() is meaningfully
  3475.           overridden.
  3476.  
  3477.      int save(const char * filename);
  3478.  
  3479.           Opens a file stream and calls put() to save the container
  3480.           state (maxNodes, curNode, flags, etcetera) and its nodes. 
  3481.           Template and form instances promote this function to
  3482.           public scope because only when the data type is known can
  3483.           the container persist, i.e. putD() is meaningfully
  3484.           overridden.
  3485.  
  3486.      inline ostream& operator<<(ostream& os,CL<ITEM>& b)
  3487.           { if (b.Flags(CL_SAVE)) (void) b.put(os); return os; }
  3488.      inline istream& operator>>(istream& is, CL<ITEM>& b)
  3489.           { (void) b.get(is); return is; }
  3490.  
  3491.           These friendly, public, stream operators are defined by
  3492.           (form) template wrappers.  ITEM is replaced with the data
  3493.           type being checking for.
  3494.  
  3495.      void vforEach(voidApplY B, va_list args);
  3496.  
  3497.           See forEach() in public scope.
  3498.  
  3499.  
  3500. Constructors and Destructor
  3501.  
  3502. Methods, public:
  3503.  
  3504.      cl (unsigned flags = CL_BIND_ONLY,
  3505.           unsigned maxNodes = CL_MAXNODES,
  3506.           unsigned limit = CL_LIMIT,
  3507.           unsigned delta = CL_DELTA)
  3508.           { init(flags,maxNodes,limit,delta); }
  3509.  
  3510.           This constructor defaults to initializing a container so that
  3511.           node assigning, cloning, and deleting methods are inhibited
  3512.           (CL_BIND_ONLY), e.g. atGetAsg(), atInsNew(), atDel(),
  3513.           etcetera, see cl::flags.  Thus the destructor will call
  3514.           allRmv() instead of allDel().  Don't leave dynamically
  3515.           allocated nodes in the container upon destruction unless the
  3516.           CL_DEL is set or they will be left dangling, i.e. a memory
  3517.           leak!  Likewise don't bind statically allocated nodes in a
  3518.           container that has its CL_DEL flag set or the destructor
  3519.           will try to de-allocate them!
  3520.  
  3521.           The container is limited to "maxNodes" starting off with an
  3522.           internal physical array big enough to hold "limit" nodes
  3523.           which will grow or shrink at "delta" rate.  CL_LIMIT is
  3524.           defined as 20 and CL_DELTA as 10 while
  3525.           CL_MAXNODES is defined as ((unsigned) (UINT_MAX /
  3526.           sizeof(void *))).
  3527.  
  3528.      cl (void ** argv, unsigned argc = 0U,
  3529.           unsigned flags = CL_BIND_ONLY);
  3530.  
  3531.           This constructor takes "argv", a vector of void pointers,
  3532.           and builds a container by copying each void pointer into a
  3533.           cell of the container's array.  If "argc" is zero then "argv"
  3534.           is expected to be NULL terminated.  This constructor calls
  3535.           init() to do the actual container initialization then proceeds
  3536.           to loop through the vector queuing the void pointers. 
  3537.           (Form) template instances strongly type check "argv."
  3538.  
  3539.      cl&  operator=(const cl& b)  { return *this; }
  3540.  
  3541.           The container assignment operator is overloaded in the
  3542.           (form) template wrapper class and thus allows the
  3543.           assignment of one container to another.  Both containers
  3544.           have to be of the same type and the "New" methods must
  3545.           be enabled in the target container, i.e. CL_NEW flag set. 
  3546.           The nodes are cleared from the target container and the
  3547.           nodes are cloned from the source container.
  3548.  
  3549.      int  operator==(const cl& b) const;
  3550.  
  3551.           The container equality operator isn't functional for a type-
  3552.           less container unless a user defined compare function has
  3553.           been set with setCmP().  This operator compares node by
  3554.           node.  It is possible to recursively compare containers of
  3555.           containers with this operator.
  3556.  
  3557.      int  operator> (const cl& b) const;
  3558.  
  3559.           The container greater than operator isn't functional for a
  3560.           type-less container unless a user defined compare function
  3561.           has been set with setCmP().  This operator compares node
  3562.           by node.  It is possible to recursively compare containers of
  3563.           containers with this operator.
  3564.  
  3565.      void ** vector(void ** argv = (void **) 0,
  3566.                unsigned argc = 0U);
  3567.  
  3568.           Returns a dynamically allocated, NULL terminated, vector
  3569.           of void pointers to the nodes currently in the container if
  3570.           argv is NULL otherwise the supplied vector is used and
  3571.           returned.  If you supply argv and argc is zero, it is
  3572.           assumed that argv is big enough.  Only if argc is non zero
  3573.           is any remaining space in argv that is not filled by node
  3574.           pointers, NULL filled.  However dynamic vectors are
  3575.           always NULL terminated.  The order of nodes in the
  3576.           container dictates the order of pointers in the vector. 
  3577.           Remember to delete the returned vector when you are done
  3578.           with it if you allowed vector() to allocate it!  The nodes of
  3579.           the container are not alerted to the fact that they are pointed
  3580.           to by this vector, i.e. attachD() is not called - attachD() is
  3581.           only called to "attach" data to a container.  Thus the
  3582.           Streamable/Mutual nodes in a container are not linked in
  3583.           any way to the returned vector!
  3584.  
  3585.      virtual ~cl()  { destruct(); }
  3586.  
  3587.           See the entry for destruct() near the beginning of the
  3588.           protected section.
  3589.  
  3590.  
  3591. Housekeeping
  3592.  
  3593. Methods public:
  3594.  
  3595.      unsigned Limit()  { return limit; }
  3596.  
  3597.           Returns the number of cells (in use plus available) in the
  3598.           container's internal physical array.
  3599.  
  3600.      unsigned setLimit(unsigned newLimit);
  3601.  
  3602.           Adjusts the size of the container's internal physical array if
  3603.           possible returning the new limit or zero otherwise.  It's not
  3604.           possible to set the new limit below the number of nodes
  3605.           currently in the container or below delta or above
  3606.           maxNodes.
  3607.  
  3608.      unsigned pack()  { return setLimit(nodes); }
  3609.  
  3610.           Shrink the container's internal physical array to be just big
  3611.           enough to hold the nodes.  Returns the number of nodes in
  3612.           the container which is also the size of the container's
  3613.           internal physical array if successful, otherwise zero is
  3614.           returned.  The logical array is always contiguous within the
  3615.           physical array, i.e. no holes.  The container may map the
  3616.           logical array's index 0 to any physical index, wrapping the
  3617.           logical array as necessary.  However, after a successful
  3618.           pack(), the logical and physical arrays are identical, i.e. no
  3619.           wrap.
  3620.  
  3621.      unsigned Delta()  { return delta; }
  3622.  
  3623.           Returns the size that the container will grow or shrink upon
  3624.           overflow or underflow respectively.
  3625.  
  3626.      unsigned setDelta(unsigned newDelta = CL_DELTA);
  3627.  
  3628.           Set the granularity of growth/shrinkage for the container's
  3629.           physical array.  CL_DELTA is defined as 10.  Returns the
  3630.           value of the new delta if successful otherwise zero is
  3631.           returned.
  3632.  
  3633.      unsigned Nodes()  { return nodes; }
  3634.  
  3635.           Returns the number of nodes currently bound within the
  3636.           container, i.e. size of the logical array.
  3637.  
  3638.      unsigned MaxNodes()  { return maxNodes; }
  3639.  
  3640.           Returns the maximum number of nodes the container is
  3641.           ever allowed to hold.
  3642.  
  3643.      unsigned setMaxNodes(unsigned newMaxNodes = CL_MAXNODES);
  3644.  
  3645.           Set the maximum number of nodes a container is allowed
  3646.           to hold returning this new value.  NewMaxNodes must not
  3647.           be below "limit" (current size of the container's internal
  3648.           physical array) or above CL_MAXNODES in order to be
  3649.           valid.  Zero is returned if newMaxNodes is invalid. 
  3650.           CL_MAXNODES is defined as ((unsigned) (UINT_MAX /
  3651.           sizeof(void *))).
  3652.  
  3653.      unsigned vacancy()  { return maxNodes - nodes; }
  3654.  
  3655.           Returns the number of nodes that can yet be added to the
  3656.           container.
  3657.  
  3658.      unsigned vacancyNonElastic()  { return limit - nodes; }
  3659.  
  3660.           Returns the number of nodes that can still be added to the
  3661.           container without undergoing an expansion of the
  3662.           container's internal physical array.
  3663.  
  3664.      unsigned Flags(unsigned flags = CL_ALL_FLAGS)
  3665.                { return (this->flags & flags); }
  3666.  
  3667.           Returns the specified flag(s).  CL_ALL_FLAGS masks all
  3668.           flags.  For flag definitions see "flags" in the protected
  3669.           scope section.
  3670.  
  3671.      unsigned setFlags(unsigned flags)
  3672.                { return (this->flags |= flags); }
  3673.  
  3674.           Allows you to raise any container flag(s).  Be sure you
  3675.           don't set the CL_DEL flag if the container has statically
  3676.           allocated nodes.  It is sometimes useful to sort the container
  3677.           using one compare function then scan it with a slightly
  3678.           different "filter" compare function.  Upon setting this new
  3679.           compare function the CL_SORTED flag is automatically
  3680.           reset but you can set it again with setFlags()!
  3681.  
  3682.      unsigned resetFlags(unsigned flags)
  3683.                { return (this->flags &= ~flags); }
  3684.  
  3685.           Allows you to reset any container flag(s).  Be sure you
  3686.           don't reset the CL_DEL flag if you are counting on the
  3687.           container's deleting any remaining nodes.  The container's
  3688.           destructor calls allRmv() unless the CL_DEL flag is set in
  3689.           which case it calls allDel().
  3690.  
  3691.      cl&  operator<<(cl& (*manipulator)(cl&))
  3692.                { return (manipulator?
  3693.                     (*manipulator)(*this)
  3694.                     : *this); }
  3695.  
  3696.           This overloaded operator allows user defined manipulators
  3697.           to be applied to a container in the same fashion iomanip.h
  3698.           allows manipulators for streams.  For example:
  3699.  
  3700.                // examp601.cpp - link with cl.obj
  3701.  
  3702.                #include "cl.hpp"
  3703.  
  3704.                cl& clr(cl& b)
  3705.                     { b.allClr(); return b; }
  3706.  
  3707.                char *v[] = {"line one ", "line two", 0 };
  3708.  
  3709.                main()
  3710.                {
  3711.                     cl b ((void **) v);
  3712.                     b << clr;
  3713.                     b.atIns(0,"only line");
  3714.                     while (++b)
  3715.                          cout << (char *) b.get() << "\n";
  3716.                     return 0;
  3717.                }
  3718.  
  3719.  
  3720. Elastic Array
  3721.  
  3722. Methods, public:
  3723.  
  3724.      void * atIns(unsigned n, void * D);
  3725.  
  3726.           Inserts a new cell at index n pushing back all cells starting
  3727.           at n possibly expanding the container's internal physical
  3728.           array if necessary.  The specified void pointer is copied to
  3729.           the new nth cell and is also returned.  If the operation fails
  3730.           the container is left unchanged and NULL is returned.  If
  3731.           D is NULL than a cell is not added.
  3732.  
  3733.      void * atInsNew(unsigned n, const void * D);
  3734.  
  3735.           Same as atIns() except the data pointed to by D is cloned
  3736.           via newD() and the pointer to the clone is inserted instead
  3737.           of the original.  The inserted pointer is returned if all goes
  3738.           well otherwise NULL is returned.  The CL_NEW flag
  3739.           must be set to enable this method.  The default newD() for
  3740.           a typeless container always returns NULL causing this
  3741.           method to fail even if the CL_NEW flag is set. 
  3742.  
  3743.      void * atRmv(unsigned n);
  3744.  
  3745.           Return the node pointer stored in the nth cell destroying
  3746.           this position pulling all cells starting at n + 1 forward
  3747.           perhaps collapsing the container's internal physical array if
  3748.           the remaining number of cells falls below an internally
  3749.           calculated threshold (lowThreshold).  If the operation fails,
  3750.           e.g. n is out of range, NULL is returned.
  3751.  
  3752.      void allRmv();
  3753.  
  3754.           Destroys all the cell positions discarding the node pointers
  3755.           without deleting the nodes.
  3756.  
  3757.      int  atDel(unsigned n);
  3758.  
  3759.           The CL_DEL flag must be set to enable this method. 
  3760.           Performs the same function as atRmv() except the discarded
  3761.           node is deleted via the deleteD() protected virtual function. 
  3762.           A non zero value is returned to indicate success.
  3763.  
  3764.      void * atDelAsg(unsigned n, void * D);
  3765.  
  3766.           The CL_DEL and CL_ASG flags must both be set to
  3767.           enable this method.  Performs the same function as atDel()
  3768.           except the contents of the node is copied to the D
  3769.           parameter location before the node is deleted.  The copy
  3770.           operation is performed by the assignD() protected virtual
  3771.           function.  If the D parameter is NULL, or the assignD()
  3772.           function fails, or for any other reason, e.g. n is out of
  3773.           range, etc., the method fails and NULL is returned. 
  3774.           Otherwise the value of the D parameter is returned.  The
  3775.           default assignD() for a typeless container always returns a
  3776.           failure indication.
  3777.  
  3778.      int  allDel();
  3779.  
  3780.           Repeatedly calls atDel(0) if the CL_DEL flag is set.  If the
  3781.           CL_DEL flag is not set, zero is returned to indicate failure.
  3782.  
  3783.      void allClr();
  3784.  
  3785.           If the CL_DEL flag is set allDel() is called, otherwise
  3786.           allRmv() is called.
  3787.  
  3788.      void * atPut(unsigned n, void * D);
  3789.  
  3790.           Overwrites the nth cell's pointer with D, the new node
  3791.           pointer, returning D.  If D is NULL or n is out of range
  3792.           then nothing is overwritten and NULL is returned.  Before
  3793.           the old pointer is overwritten, it is detached via detachD()
  3794.           and deleted via deleteD if the CL_DEL flag is set.  The
  3795.           new node is of course attached via attachD().
  3796.  
  3797.      void * atPutNew(unsigned n, const void * D);
  3798.  
  3799.           The CL_NEW flag must be set to enable this method. 
  3800.           Performs the same function as atPut() except the data
  3801.           pointed to by D is cloned via newD() and the pointer to the
  3802.           clone is atPut()'ed.  If successful the clone's pointer is
  3803.           returned, otherwise NULL is returned.
  3804.  
  3805.      void * atPutAsg(unsigned n, const void * D);
  3806.  
  3807.           The CL_ASG flag must be set to enable this method. 
  3808.           Copies the data pointed to by the D parameter to the node
  3809.           bound at index n.  The actual copying is performed by the
  3810.           assignD() protected virtual function.  If successful, the
  3811.           pointer to the bound node is returned.  The default
  3812.           assignD() for a typeless container always returns a failure
  3813.           indication.
  3814.  
  3815.      void * atGet(unsigned n);
  3816.  
  3817.           Returns the node pointer stored in the nth cell.
  3818.  
  3819.      void * operator[](unsigned n)  { return atGet(n); }
  3820.  
  3821.           As shown by its inline definition, the overloaded
  3822.           subscripting operator provides a convenient notation for
  3823.           calling atGet().
  3824.  
  3825.      void * atGetAsg(unsigned n, void * D);
  3826.  
  3827.           Provides the same functionality as atGet(), except the
  3828.           contents of the node bound at index n is copied via
  3829.           assignD() to the location pointed to by the D parameter.  If
  3830.           successful the value of the D parameter is returned.  The
  3831.           CL_ASG flag must be set to enable this method.  Also, the
  3832.           default assignD() for a typeless container always returns a
  3833.           failure indication.
  3834.  
  3835.      void * atXchg(unsigned n, void * D);
  3836.  
  3837.           Returns the node pointer stored in the nth cell if D is not
  3838.           NULL and n is in range otherwise NULL is returned.  D is
  3839.           written to the nth cell.
  3840.  
  3841.      unsigned index(const void * D);
  3842.  
  3843.           Find the index of the cell containing the pointer with the
  3844.           same value as D.  If D is not bound in the container then
  3845.           index() returns CL_NOTFOUND which is defined as
  3846.           CL_MAXNODES which is in turn defined as ((unsigned)
  3847.           (UINT_MAX / sizeof(void *))).  Since the cells of a
  3848.           container are indexed from 0 to nodes - 1,
  3849.           CL_NOTFOUND will never be an index of a valid
  3850.           container cell.
  3851.  
  3852.      void forEach(voidApplY B, ...);
  3853.  
  3854.           This container iterator applies the function specified by the
  3855.           function pointed to by B to each of its nodes.  The function
  3856.           pointer type is defined as:
  3857.  
  3858.                typedef void (*voidApplY)(void * D,
  3859.                va_list args);
  3860.  
  3861.           Template and form instances automatically type the D
  3862.           parameter to the correct data type so there is no need to
  3863.           type cast your function pointer to type voidApplY if it is of
  3864.           the following form:
  3865.  
  3866.                void (*B)(ITEM * D, va_list args);
  3867.  
  3868.      int detect(void *& D, voidDetecT B, void * V = 0);
  3869.  
  3870.           This container iterator applies the function pointed to by B
  3871.           to each of its nodes.  The first node "detected", i.e. the
  3872.           function (*B)(...) returns a non zero value, causes the
  3873.           detect() iterator to return immediately with a non zero
  3874.           value.  The D pointer points to the detected node in this
  3875.           case, otherwise it is set to NULL. The function pointer
  3876.           type is defined as:
  3877.  
  3878.                typedef int (*voidDetecT)(void * D, void * V);
  3879.  
  3880.           Template and form instances automatically type the D
  3881.           parameter to the correct data type so there is no need to
  3882.           type cast your function pointer to type voidDetecT if it is
  3883.           of the following form:
  3884.  
  3885.                int (*B)(ITEM * D, void * V);
  3886.  
  3887.           The V parameter allows an additional user defined
  3888.           parameter to be passed to the detecting function.
  3889.  
  3890.      int detect(unsigned& i, voidDetecT B, void * V = 0);
  3891.  
  3892.           This container iterator applies the function pointed to by B
  3893.           to each of its nodes.  The first node "detected", i.e. the
  3894.           function (*B)(...) returns a non zero value, causes the
  3895.           detect() iterator to return immediately with a non zero
  3896.           value.  The i integer is set to the index of the detected node
  3897.           in this case, otherwise it is set to CL_NOTFOUND. The
  3898.           function pointer type is defined as:
  3899.  
  3900.                typedef int (*voidDetecT)(void * D, void * V);
  3901.  
  3902.           Template and form instances automatically type the D
  3903.           parameter to the correct data type so there is no need to
  3904.           type cast your function pointer to type voidDetecT if it is
  3905.           of the following form:
  3906.  
  3907.                int (*B)(ITEM * D, void * V);
  3908.  
  3909.           The V parameter allows an additional user defined
  3910.           parameter to be passed to the detecting function.
  3911.  
  3912.      unsigned select(cl& c, voidDetecT B, void * V = 0);
  3913.  
  3914.           This container iterator applies the function pointed to by B
  3915.           to each of its nodes.  Whenever a node is "detected", i.e.
  3916.           the function (*B)(...) returns a non zero value, that node is
  3917.           queued into the "c" container.  The "c" container is in
  3918.           effect a list of the nodes "detected."  This iterator returns
  3919.           zero if no nodes are detected, otherwise the number of
  3920.           nodes detected is returned.  Thus the return value of
  3921.           select() can be considered as a boolean value with true
  3922.           indicating that one or more nodes have been selected.  The
  3923.           function pointer type is defined as:
  3924.  
  3925.                typedef int (*voidDetecT)(void * D, void * V);
  3926.  
  3927.           Template and form instances automatically type the D
  3928.           parameter to the correct data type so there is no need to
  3929.           type cast your function pointer to type voidDetecT if it is
  3930.           of the following form:
  3931.  
  3932.                int (*B)(ITEM * D, void * V);
  3933.  
  3934.           The V parameter allows an additional user defined
  3935.           parameter to be passed to the detecting function.
  3936.  
  3937.      unsigned reject(cl& c, voidDetecT B, void * V = 0);
  3938.  
  3939.           This container iterator applies the function pointed to by B
  3940.           to each of its nodes.  Whenever a node is not "detected",
  3941.           i.e. the function (*B)(...) returns a zero value, that node is
  3942.           queued into the "c" container.  The "c" container is in
  3943.           effect a list of the nodes "detected."  This iterator returns
  3944.           zero if no nodes are detected, otherwise the number of
  3945.           nodes detected is returned.  Thus the return value of
  3946.           select() can be considered as a boolean value with true
  3947.           indicating that one or more nodes have been selected.  The
  3948.           function pointer type is defined as:
  3949.  
  3950.                typedef int (*voidDetecT)(void * D, void * V);
  3951.  
  3952.           Template and form instances automatically type the D
  3953.           parameter to the correct data type so there is no need to
  3954.           type cast your function pointer to type voidDetecT if it is
  3955.           of the following form:
  3956.  
  3957.                int (*B)(ITEM * D, void * V);
  3958.  
  3959.           The V parameter allows an additional user defined
  3960.           parameter to be passed to the detecting function.
  3961.  
  3962.      unsigned collect(cl& c, voidCollecT B, void * V = 0);
  3963.  
  3964.           This container iterator applies the function pointed to by B
  3965.           to each of its nodes.  All the non NULL pointers returned
  3966.           by (*B)(...) are collected, i.e. queued, into the "c"
  3967.           container.  This iterator returns zero if no pointers are
  3968.           collected, otherwise the number of pointers collected is
  3969.           returned.  Thus the return value of collect() can be
  3970.           considered as a boolean value with true indicating that one
  3971.           or more pointers have been selected.  The function pointer
  3972.           type is defined as:
  3973.  
  3974.                typedef void * (*voidCollecT)(void * D, void * V);
  3975.  
  3976.           Template and form instances automatically type the D
  3977.           parameter to the correct data type so there is no need to
  3978.           type cast your function pointer to type voidCollecT if it is
  3979.           of the following form:
  3980.  
  3981.                void * (*B)(ITEM * D, void * V);
  3982.  
  3983.           The V parameter allows an additional user defined
  3984.           parameter to be passed to the collecting function.
  3985.  
  3986.  
  3987. Stack, Queue, and Deque
  3988.  
  3989. Methods, public:
  3990.  
  3991.      void * push(void * D)  { return atIns(0,D); }
  3992.  
  3993.           Pushes the node pointer onto the stack returning that
  3994.           pointer if successful otherwise it returns the NULL pointer.
  3995.  
  3996.      void * pushNew(const void * D)  { return atInsNew(0,D); }
  3997.  
  3998.           Same as push() except the data pointed to by D is cloned
  3999.           and a pointer to the clone is instead pushed.
  4000.  
  4001.      void * pop()  { return atRmv(0); }
  4002.  
  4003.           Pop the stack returning the node pointer or NULL if no
  4004.           nodes are present.
  4005.  
  4006.      cl&  operator>>(void *& D)  { D = atRmv(0); return *this; }
  4007.  
  4008.           Provides a convenient way to call pop().
  4009.  
  4010.      int  popDel()  { return atDel(0); }
  4011.  
  4012.           Same as pop() except the popped pointer is deleted via the
  4013.           deleteD() function.  The CL_DEL flag must be set to
  4014.           enable this method.
  4015.  
  4016.      void * popDelAsg(void * D)  { return atDelAsg(0,D); }
  4017.  
  4018.           Same as popDel() except the contents of the node being
  4019.           delete is copied via assignD() to the location pointed to by
  4020.           D.  If successful D is returned otherwise NULL is.  The
  4021.           CL_ASG and CL_DEL flags both must be set to enable this
  4022.           method.
  4023.  
  4024.      void * top()  { return atGet(0); }
  4025.  
  4026.           Returns the pointer to the node at the front of the container
  4027.           or NULL if there are no nodes.  The front of the container
  4028.           is considered to be the top of the stack or the front of the
  4029.           deque-queue.
  4030.  
  4031.      void * topAsg(void * D)  { return atGetAsg(0,D); }
  4032.  
  4033.           Same as top() except the contents of the top node is copied
  4034.           via the assignD() virtual function to the location pointed to
  4035.           by D.  If successful D is returned.  The CL_ASG flag must
  4036.           be set to enable this method.
  4037.  
  4038.      void * insQ(void * D)  { return atIns(nodes,D); }
  4039.  
  4040.           Inserts the D pointer at the rear of the queue returning this
  4041.           pointer if successful or NULL otherwise.
  4042.  
  4043.      cl& operator<<(void * D)  { atIns(nodes,D); return *this; }
  4044.  
  4045.           This overloaded operator provides a convenient notation for
  4046.           inserting into the queue.
  4047.  
  4048.      void * insQNew(const void * D)  { return atInsNew(nodes,D); }
  4049.  
  4050.           Same as insQ() except what D points to is cloned via the
  4051.           newD() protected virtual function and the pointer to the
  4052.           clone is queued instead of D.  If successful the pointer to
  4053.           the clone is returned.  The CL_NEW flag must be set to
  4054.           enable this method.
  4055.  
  4056.      void * unQ()  { return atRmv(nodes-1); }
  4057.  
  4058.           Pops the rear of the queue returning its pointer.
  4059.  
  4060.      int  unQDel()  { return atDel(nodes-1); }
  4061.  
  4062.           Same as unQ() except the popped node is deleted via the
  4063.           deleteD() virtual function.  Zero is returned to indicate
  4064.           failure.  The CL_DEL flag must be set to enable this
  4065.           method.
  4066.  
  4067.      void * unQDelAsg(void * D)  { return atDelAsg(nodes-1,D); }
  4068.  
  4069.           Same as unQDel() except before deleting the popped node
  4070.           its contents are copied via the assignD() virtual function to
  4071.           the location pointed to by D.  If successful D is returned. 
  4072.           Both the CL_ASG and CL_DEL flags have to be set to
  4073.           enable this method.
  4074.  
  4075.      void * rear()  { return atGet(nodes-1); }
  4076.  
  4077.           Returns a pointer to the rear node of the queue.
  4078.  
  4079.      void * rearAsg(void * D)  { return atGetAsg(nodes-1,D); }
  4080.  
  4081.           Same as rear() except the contents of the rear node is
  4082.           copied via the assignD() virtual function to the location
  4083.           pointed to by D.  If successful D is returned otherwise
  4084.           NULL is returned.  The CL_ASG flag must be set to
  4085.           enable this method.
  4086.  
  4087.  
  4088. List
  4089.  
  4090. Methods, public:
  4091.  
  4092.      unsigned CurNode();
  4093.  
  4094.           Returns the index to the list's current node if there is one
  4095.           or CL_NOTFOUND if the current node is undefined. 
  4096.           When a container is first constructed, its current node is
  4097.           undefined. 
  4098.  
  4099.      int  setCurNode(unsigned n = CL_NOTFOUND);
  4100.  
  4101.           Resets the list's internal current node index to n if within
  4102.           range, i.e. 0 to nodes-1, or to undefined if not. 
  4103.           CL_NOTFOUND is never in range so the current node is
  4104.           left as undefined if setCurNode() is called without a
  4105.           parameter.  Returns false only if current node is left
  4106.           undefined.
  4107.  
  4108.      void * ins(void * D);
  4109.  
  4110.           Inserts D into the container after the list's current node
  4111.           making the inserted pointer, in the newly created cell, the
  4112.           new current node.  Returns D if successful, otherwise it
  4113.           returns NULL.  If at the outset, the current node is
  4114.           undefined then the node is inserted at the rear of the list
  4115.           and made the new current node.  Remember, a list may or
  4116.           may not have a current node defined.
  4117.  
  4118.      void * insNew(const void * D);
  4119.  
  4120.           Same as ins() except what D points to is cloned via the
  4121.           newD() virtual function and the pointer to the clone is
  4122.           inserted.  If successful the pointer to the clone is returned
  4123.           otherwise the NULL pointer is returned.  The CL_NEW
  4124.           flag must be set to enable this method.
  4125.  
  4126.      void * rmv();
  4127.  
  4128.           Destroys the current node cell returning its pointer.  The
  4129.           current node setting is decremented making the node ahead
  4130.           of the node just extracted current. If the first node of the
  4131.           list is extracted the current node index is left unchanged.  If
  4132.           the first node is also the last node the current node becomes
  4133.           undefined.  If the operation fails for any reason, e.g.
  4134.           current node undefined at the outset or no nodes in the list,
  4135.           NULL is returned.
  4136.  
  4137.      int  del();
  4138.  
  4139.           Same as rmv() except the removed node is deleted via the
  4140.           deleteD() protected virtual function.  Zero is returned to
  4141.           indicate failure.  The CL_DEL flag must be set to enable
  4142.           this method.
  4143.  
  4144.      void * delAsg(void * D);
  4145.  
  4146.           Same as del() except the contents of the node is copied via
  4147.           the assignD() protected virtual function to the location
  4148.           pointed to by D before the node is itself is deleted.  If
  4149.           successful D is returned.  Both the CL_ASG and CL_DEL
  4150.           flags must be set to enable this method.
  4151.  
  4152.      void * put(void * D)  { return atPut(curNode,D); }
  4153.  
  4154.           Overwrites the current node's pointer with D, the new node
  4155.           pointer, returning D.  If D is NULL or the current node
  4156.           index is undefined then nothing is overwritten and NULL is
  4157.           returned.  Before the old pointer is overwritten, it is
  4158.           deleted via deleteD() if the CL_DEL flag is set.
  4159.  
  4160.      void * putNew(const void * D)  { return atPutNew(curNode,D); }
  4161.  
  4162.           The CL_NEW flag must be set to enable this method. 
  4163.           Performs the same function as put() except the data pointed
  4164.           to by D is cloned via newD() and the pointer to the clone is
  4165.           put.  If successful the clone's pointer is returned, otherwise
  4166.           NULL is returned.
  4167.  
  4168.      void * putAsg(const void * D)  { return atPutAsg(curNode,D); }
  4169.  
  4170.           The CL_ASG flag must be set to enable this method. 
  4171.           Copies the data pointed to by the D parameter to the
  4172.           current node.  The actual copying is performed by the
  4173.           assignD() protected virtual function.  If successful, the
  4174.           pointer to the bound node is returned.
  4175.  
  4176.      void * get()  { return atGet(curNode); }
  4177.  
  4178.           Returns the pointer to the list's current node or NULL if no
  4179.           node is current or there are no nodes.
  4180.  
  4181.      operator TYPE *()  { return atGet(curNode); }
  4182.  
  4183.           The subscript operator is also overloaded as shown here as
  4184.           a convenient method for calling atGet() but only for (form)
  4185.           template container wrappers.  TYPE is replaced with the
  4186.           data type being checked for.
  4187.  
  4188.      void * getAsg(void * D)  { return atGetAsg(curNode,D); }
  4189.  
  4190.           Same as get() except the current node is copied via the
  4191.           assignD() protected virtual function to the location pointed
  4192.           to by D.  If successful D is returned.  The CL_ASG flag
  4193.           must be set to enable this method.
  4194.  
  4195.      void * next();
  4196.  
  4197.           Advances the list's current node index returning the pointer
  4198.           to the next node.  Upon reaching the end of the list next()
  4199.           returns NULL.  A subsequent call to next() causes the
  4200.           current node index to wrap around to zero, the index of the
  4201.           first node in the list, and the process of walking the list can
  4202.           begin anew.
  4203.  
  4204.      void * operator++()  { return next(); }
  4205.  
  4206.           This overloaded prefix operator provides a convenient
  4207.           notation for calling next().
  4208.  
  4209.      void * nextAsg(void * D);
  4210.  
  4211.           Same as next() except the contents of the new current node
  4212.           is copied via the assignD() protected virtual function to the
  4213.           location pointed to by D.  If successful D is returned.  The
  4214.           CL_ASG flag must be set to enable this method.
  4215.  
  4216.      void * prev();
  4217.  
  4218.           Decrements the list's current node index returning the
  4219.           pointer to the new current node.  After reaching the front
  4220.           of the list, the next call to prev() returns NULL.  A
  4221.           subsequent call to prev() causes the current node index to
  4222.           wrap around positioning itself on the last node of the list
  4223.           and the process of walking backwards across the list can
  4224.           begin anew.
  4225.  
  4226.      void * operator--()  { return prev(); }
  4227.  
  4228.           This overloaded prefix operator provides a convenient
  4229.           notation for calling prev().
  4230.  
  4231.      void * prevAsg(void * D);
  4232.  
  4233.           Same as prev() except the contents of the current node is
  4234.           copied via the assignD() protected virtual function to the
  4235.           location pointed to by D.  If successful D is returned.  The
  4236.           CL_ASG flag must be set to enable this method.
  4237.  
  4238.  
  4239. Sort, Search, and Unique
  4240.  
  4241. Methods, public:
  4242.  
  4243.      unsigned Sorted()  { return (flags & CL_SORTED); }
  4244.  
  4245.           Returns true if the container is still sorted since its last sort. 
  4246.           For example, if a container was sorted and then nodes
  4247.           popped from it, it would still be sorted so Sorted() would
  4248.           return true.  But if it had been pushed since the last sort,
  4249.           the sorted order couldn't still be guaranteed so Sorted()
  4250.           would return false.  It is possible to disturb a container's
  4251.           sorted order without the container being able to detect it,
  4252.           e.g. using the pointer returned from atGet() to modify a
  4253.           key field of a node while it is still bound within the
  4254.           container.  In such cases you should call unSort() to let the
  4255.           container know that it can no longer guarantee a sorted
  4256.           order.
  4257.  
  4258.      void unSort()  { flags &= ~CL_SORTED; }
  4259.  
  4260.           See explanation of Sorted().
  4261.  
  4262.      void setCmP(voidCmP cmP = voidCmP0)
  4263.                { this->cmP = cmPD(cmP); flags &= ~CL_SORTED; }
  4264.  
  4265.           Sets the container's compare function which is used by
  4266.           sort(), insSort(), insUnique(), findFirst(), etc..  The
  4267.           compare function must return zero to indicate a match and
  4268.           a value greater than zero if its first parameter is greater
  4269.           than its second.  If cmP is NULL cmPD() will return a
  4270.           default compare function for template and form instances. 
  4271.           Your compare function should be of the form:
  4272.  
  4273.                int cmp(const TYPE *, const TYPE *);
  4274.  
  4275.      voidCmP  CmP()  { return cmP; }
  4276.  
  4277.           Returns the compare function pointer stored in the
  4278.           container's header.
  4279.  
  4280.      int  sort(voidCmP cmP = voidCmP0);
  4281.  
  4282.           Uses a binary sort to sort the list in conjunction with the
  4283.           user supplied compare function.  If the compare function
  4284.           pointer is NULL then the previously set compare function
  4285.           pointer is used.  If the previous function pointer is NULL
  4286.           and cmPD() is unable to provide a default then the list
  4287.           obviously can't be sorted and false is returned.
  4288.  
  4289.      void * insSort(void * D);
  4290.  
  4291.           Uses the previously set compare function pointer to insert
  4292.           the D pointer into the container in a sorted order according
  4293.           to the key contained in the node pointed to by D.  What is
  4294.           or isn't a key is defined by the compare function.  If the
  4295.           list isn't sorted, it is first sorted and then the insertion sort
  4296.           of D takes place.  In either case the newly insert node
  4297.           becomes the list's new current node.  If either the compare
  4298.           function pointer (no default) or D is NULL then the
  4299.           method fails and NULL is returned and the current node is
  4300.           left undefined.  Otherwise D is returned.
  4301.  
  4302.      void * insSortNew(const void * D);
  4303.  
  4304.           Same as insSort() except what D points to is cloned via the
  4305.           newD() protected virtual function and the clone is inserted
  4306.           in sorted order.  If successful a pointer to the clone is
  4307.           returned.  The CL_NEW flag must be set to enable this
  4308.           method.
  4309.  
  4310.      void * insUnique(void * D);
  4311.  
  4312.           Calls findFirst() to see if there is a match for D.  If not D
  4313.           is inserted into the container via insSort().
  4314.  
  4315.      void * insUniqueNew(const void * D);
  4316.  
  4317.           Same as insUnique() except D is cloned via newD() and the
  4318.           cloned inserted in sorted order.  If successful the pointer to
  4319.           the clone is returned otherwise NULL is returned.  The
  4320.           CL_NEW flag must be set to enable this method.
  4321.  
  4322.      void * findFirst(const void * K, cl::RelCmp rc = EQ);
  4323.  
  4324.           Uses the previously set compare function (see setCmP()) to
  4325.           find a node matching K.  If the compare function hasn't
  4326.           been established a default one is provided if available from
  4327.           a template or form definition.  Since the compare function
  4328.           is user definable any field in the node can be used as a key. 
  4329.           The compare function is called with the node being tested
  4330.           as its second parameter and K as its first.
  4331.  
  4332.           If the list is sorted then findFirst() uses a binary search to
  4333.           find the first match, otherwise it uses a linear search.  If all
  4334.           goes well the pointer to the matching node is returned
  4335.           otherwise NULL is returned.  The current node index is set
  4336.           to that of the matching node or to undefined if no match is
  4337.           found.
  4338.  
  4339.           The compare function must always be defined so that it
  4340.           returns zero to indicate equality and a value greater than
  4341.           zero if its first parameter is greater than its second.  The rc
  4342.           parameter allows the sense of the match to be modified. 
  4343.           For example, if rc is set to GT then findFirst() will match
  4344.           the first node greater than K.  RelCmp is an enumerated
  4345.           type defined as:
  4346.  
  4347.                enum RelCmp { EQ, NE, GT, GE, LT, LE };
  4348.  
  4349.      ITEM *  operator[](const ITEM * K)  { return findFirst(K); }
  4350.  
  4351.           As you can see from its inline definition, this overloaded
  4352.           subscript operator is a convenient way to call findfirst().  It
  4353.           is only defined for (form) template wrappers where ITEM
  4354.           is the data type being type checked.
  4355.  
  4356.      void * findNext(const void * K, cl::RelCmp rc = EQ);
  4357.  
  4358.           If the list is sorted the next node past the list's current node
  4359.           is tested for a match with K via the previously set user
  4360.           defined compare function.  If it is a match the current node
  4361.           index is advanced and a pointer to that node is returned.  If
  4362.           it isn't, findNext() returns the NULL pointer, after setting
  4363.           the current node index to undefined.  
  4364.  
  4365.           If the list isn't sorted at the outset then the list is scanned
  4366.           starting with the first node past the current node and
  4367.           continues looking until the end of the list is reached.  If a
  4368.           match is found that node becomes the new current node and
  4369.           its pointer returned, otherwise the current node is set to
  4370.           undefined and the NULL pointer, is returned.
  4371.  
  4372.           While it is possible to call findNext() after calling
  4373.           findLast() and findPrev() several times and come up with a
  4374.           match in a sorted list, be aware that for sorted lists the
  4375.           current node must be immediately ahead or within the
  4376.           contingent of matching nodes for findNext() to find them as
  4377.           expected!  In other words for sorted lists be sure to call
  4378.           findFirst() first!
  4379.  
  4380.           The compare function must always be defined so that it
  4381.           returns zero to indicate equality and a value greater than
  4382.           zero if its first parameter is greater than its second.  The rc
  4383.           parameter allows the sense of the match to be modified. 
  4384.           For example, if rc is set to LE then findNext() matches the
  4385.           next node that is less than or equal to K.  RelCmp is an
  4386.           enumerated type defined as:
  4387.  
  4388.                enum RelCmp { EQ, NE, GT, GE, LT, LE };
  4389.  
  4390.      void * findLast(const void * K, cl::RelCmp rc = EQ);
  4391.  
  4392.           Where as findFirst() searches for its first match closest to
  4393.           the front of the list, findLast() searches for its first match
  4394.           closest to the rear of the list.  Likewise, if the list is sorted
  4395.           a binary search is made and if not a linear search is made. 
  4396.           The current node index is set to the matching node and the
  4397.           node's pointer returned.  If there are no matching nodes
  4398.           then the current node index is set to undefined and NULL
  4399.           is returned.
  4400.  
  4401.      void * findPrev(const void * K, cl::RelCmp rc = EQ);
  4402.  
  4403.           If the list is sorted the previous node in the list is tested for
  4404.           a match with K via the user defined compare function
  4405.           previously set.  If it is a match the current node index is
  4406.           decremented and a pointer to that node is returned.  If it
  4407.           isn't, findPrev() returns NULL after setting the current
  4408.           node index to undefined.  
  4409.  
  4410.           If the list isn't sorted then the list is walked backwards
  4411.           from the current node while looking for a match or until
  4412.           dropping off the front of the list.  If a match is found that
  4413.           node becomes the new current node and its pointer returned
  4414.           otherwise the current node is set to undefined and NULL is
  4415.           returned.
  4416.  
  4417.           Be sure to read the explanation for findNext().
  4418.  
  4419.      unsigned findAll(cl& b, const void * K, cl::RelCmp rc = EQ);
  4420.  
  4421.           Using the previously user defined compare function, each
  4422.           node is checked against K and the matches collected into
  4423.           the "b" container.  The current node is left undefined and
  4424.           the number of nodes collected is returned.
  4425.  
  4426.           The compare function must always be defined so that it
  4427.           returns zero to indicate equality and a value greater than
  4428.           zero if its first parameter is greater than its second.  The rc
  4429.           parameter allows the sense of the match to be modified. 
  4430.           For example, if rc is set to NE then findAll() matches all
  4431.           nodes that are not equal to K.  RelCmp is an enumerated
  4432.           type defined as:
  4433.  
  4434.                enum RelCmp { EQ, NE, GT, GE, LT, LE };
  4435.  
  4436.      unsigned tallyAll(const void * K, cl::RelCmp rc = EQ);
  4437.  
  4438.           Same as findAll() except the matched nodes are not
  4439.           collected.
  4440.  
  4441. FunctionRegistry
  4442.  
  4443.  
  4444.      The FunctionRegistry is a class that provides function pointer
  4445.      registration so that function pointers can be streamed.  A
  4446.      container's default compare function does not need to be registered
  4447.      since it can be recovered automatically.  However, your own
  4448.      defined compare functions and functions whose pointers appear in
  4449.      nodes need to be registered before streaming.  A unique id is
  4450.      associated with each function pointer and that id is what actually
  4451.      gets stream.  Once registered, a compare function that is set for a
  4452.      container is automatically streamed with that container without
  4453.      programmer intervention.  The macros below are provided so that
  4454.      you may deal with other function pointers appearing in nodes
  4455.      yourself.
  4456.  
  4457.      #define Register_CmP(cmP,id)  Register_Function(cmP,id)
  4458.  
  4459.           For user defined compare functions call Register_CmP()
  4460.           with a unique id.
  4461.  
  4462.      #define Register_Function(fnC,id)  \
  4463.           fnCv.regFnC((GenericFnC)fnC,id)
  4464.  
  4465.           For other functions that need to be streamed call
  4466.           Register_Function() with a unique id.  Be sure that you id
  4467.           doesn't conflict with any other function including compare
  4468.           functions!
  4469.  
  4470.      #define Forget_Functions()  fnCv.forget()
  4471.  
  4472.           When your application has reached a phase where no more
  4473.           function pointers will be streamed you may call
  4474.           Forget_Functions() to free up space held by the
  4475.           FunctionRegistry.
  4476.  
  4477.      #define fnC2ID(fnC)  fnCv.fnC_2_ID((GenericFnC)fnC)
  4478.  
  4479.           Once registered, use this macro to convert a function
  4480.           pointer to its id.
  4481.  
  4482.      #define ID2fnC(id)  fnCv.ID_2_fnC(id)
  4483.  
  4484.           Once registered, use this macro to convert an id to its
  4485.           function pointer.  Don't forget any type cast that may be
  4486.           required to get you back to the correct pointer type!
  4487.  
  4488.      The following example demonstrates the function pointer macros.
  4489.  
  4490.  
  4491.           // examp602.cpp - link with cl.obj
  4492.  
  4493.           #include "cl.hpp"
  4494.  
  4495.           #define ID_test  5
  4496.  
  4497.           char * test(char * s1, char *s2)
  4498.                { cout << s1; return s2; }
  4499.  
  4500.           main()  {
  4501.                Register_Function(test,ID_test);
  4502.                cout << (*(char *(*)(char *, char*))
  4503.                     ID2fnC(fnC2ID(test)))
  4504.                     ("\nGoodbye persistence headaches!\n",
  4505.                     "Hello streamable functions!\n");
  4506.                return 0;
  4507.           }
  4508.  
  4509.  
  4510. First the function pointer is registered.  Then the function pointer is
  4511. converted to an id which is converted back to a function pointer which is
  4512. invoked with the two string parameters returning a string to stream to
  4513. cout.  You can use the fnC2ID() macro to save a function pointer's id
  4514. inside your overriding cl::putD() function.  Inside getD(), the extracted id
  4515. is converted back to a function pointer with the ID2fnC() macro.
  4516.  
  4517.  
  4518. Streamable
  4519.  
  4520.  
  4521. Streamable is a conceptually abstract class that provides a basis for a
  4522. persistable polymorphic cluster of classes that is container compatible. 
  4523. The Streamable class works in concord with the ClassRegistry class.
  4524.  
  4525.  
  4526. Members, private:
  4527.  
  4528.      void * parenT;
  4529.  
  4530.           The first object to "link()" to the instance with a non
  4531.           NULL parent pointer while the instance's parenT is NULL
  4532.           will automatically establish a back link to that object. 
  4533.           When the back link parent unlink()'s, parenT is reset to
  4534.           NULL (see ParenT()).  This back link makes it easy for
  4535.           Streamable/Mutual nodes of a container to be able to access
  4536.           container member functions.
  4537.  
  4538.      void init() { parenT = (void *)0; }
  4539.  
  4540.           Initializes the parenT pointer.
  4541.  
  4542. Members, protected:
  4543.  
  4544.      void  assign(const Streamable& s)  { parenT = s.parenT; }
  4545.  
  4546.           Used to form an assignment chain in descendants for use in
  4547.           copy initializer constructors and assignment operators.  (see
  4548.           pitem.cbk)
  4549.  
  4550.      Streamable(defaultConstructor)  { init(); }
  4551.  
  4552.           Insures an unambiguous default constructor for hierarchy
  4553.           chains.  Used when reloading from stream.
  4554.  
  4555.      virtual int put(ostream&) { return 1; }
  4556.  
  4557.           Called by insert() to store the Streamable derived object on
  4558.           stream.  All descendants override this function.  Each level
  4559.           put() calls its base class put to store the base level
  4560.           members.  The parenT pointer is never saved on stream but
  4561.           instead is reconstructed in the rebuilding linking that takes
  4562.           place upon reloading.
  4563.  
  4564.      int  get(istream&)  { return 1; }
  4565.  
  4566.           Called by extract() if current level is the instance level
  4567.           otherwise it is called from a descendant's get().
  4568.  
  4569.      ostream& insert(ostream& os);
  4570.  
  4571.           Inserts id and then calls put().
  4572.  
  4573.      static    StreamablE extract(istream& is);
  4574.  
  4575.           Acts as a virtual initialize-from-stream constructor. 
  4576.           Streamable::extract() extracts id from stream and looks up
  4577.           the instance level extract() in the class registry.  The
  4578.           instance level extract() is called to allocate and default
  4579.           construct the instance and then calls the instance level get(). 
  4580.           Each derived class overloads extract() to allocate and
  4581.           default construct and to call its respective get().  It is the
  4582.           instance level extract() that is registered with a call to
  4583.           Register_Class().
  4584.  
  4585. Members, public:
  4586.  
  4587.      inline ostream& operator<<(ostream& os, Streamable& s)
  4588.                { return s.insert(os); }
  4589.      inline istream& operator>>(istream& is, StreamablE& S)
  4590.           { S = Streamable::extract(is); return is; }
  4591.  
  4592.           These stream operators are actually just friends of
  4593.           Streamable.  Notice how they call insert() and  extract(). 
  4594.           Any object derived from a Streamable hierarchy uses these
  4595.           operators for streaming.  Recall from examp502.cpp, a
  4596.           Shape overloads put() which is called from insert() above. 
  4597.           When reloading a Shape, Streamable::extract() extracts
  4598.           Shape's id which it then uses to look-up Shape::extract(). 
  4599.           Shape::extract() then allocates and default constructs Shape
  4600.           and then calls Shape::get().  In examp503.cpp, recall that a
  4601.           Circle is derived from Shape.  In this case
  4602.           Streamable::extract extracts Circle's id and looks up
  4603.           Circle::extract().  Circle::extract() allocates and default
  4604.           constructs Circle which in turn default constructs Shape. 
  4605.           Then Circle::extract() calls Circle::get() which calls
  4606.           Shape::get() which extracts X and Y coordinates and
  4607.           returns to Circle::get() which then extracts the radius which
  4608.           returns to Circle::extract() which returns the pointer to the
  4609.           newly constructed Circle.
  4610.  
  4611.      Streamable() { init(); }
  4612.  
  4613.           Standard default constructor for descendants.
  4614.  
  4615.      virtual unsigned restream()  { return 0U; }
  4616.  
  4617.           Restreaming takes on meaning with multiple referenced
  4618.           nodes.  It is declared here so that container template and
  4619.           forms can handle either Streamable or Mutual in a
  4620.           consistent manner.
  4621.  
  4622.      virtual   int operator=(const Streamable&)  { return 0; }
  4623.  
  4624.           Polymorphic cluster nodes may be assigned to one another
  4625.           if they have identical ID()'s.  (See pitem.cbk to see how
  4626.           this is done.)  Then the container's assignD() virtual
  4627.           function only has to call Streamable::operator=().
  4628.  
  4629.      virtual   StreamablE clone() const { return StreamablE0; }
  4630.  
  4631.           Polymorphic descendants of Streamable override this
  4632.           function to return a clone of themselves.  This is
  4633.           accomplished at each level by applying the new operator to
  4634.           that level's copy initializer constructor.  The container
  4635.           newD() virtual function simply calls Streamable::clone(). 
  4636.           Thus clone() acts like a virtual copy initializer constructor
  4637.           for a polymorphic cluster.
  4638.  
  4639.      virtual   unsigned ID() const { return 0U; }
  4640.  
  4641.           Polymorphic descendants of Streamable override this
  4642.           function to provide a unique id.  This may be replaced in
  4643.           subsequent versions of Container Lite if C++ gets run
  4644.           time typing.
  4645.  
  4646.      operator unsigned() const { return ID(); }
  4647.  
  4648.           A convenient method for invoking ID().
  4649.  
  4650.      virtual   unsigned level()  { return 0U; }
  4651.  
  4652.           This function is fully user definable and is not used by
  4653.           either the container or stream registry, etc..
  4654.  
  4655.      void *  ParenT() { return parenT; }
  4656.  
  4657.           Linking automatically maintains a back link pointer to the
  4658.           first linking source.  This is provided simply as a
  4659.           convenience to your application if use can be made of it.
  4660.  
  4661.      void    unlink(void * P = (void *)0)
  4662.                { if (parenT == P) parenT = (void*)0; }
  4663.  
  4664.           Called by cl::detachD() to signal the node it is no longer
  4665.           attached to the container.  Mutual overloads this function to
  4666.           provide multi-reference resolution.
  4667.  
  4668.      int     link(void * P = (void *)0)
  4669.                { if (!parenT) parenT = P; return 1; }
  4670.  
  4671.           Called by cl::attachD() to signal the node it is attached to
  4672.           the container.  In this manner a polymorphic cluster node
  4673.           can determine which container it its attached to.  Mutual
  4674.           overloads this function to provide multi-reference
  4675.           resolution.
  4676.  
  4677.      unsigned RefCount() { return 0U; }
  4678.  
  4679.           Provided for consistency with Mutual's overloaded
  4680.           RefCount().
  4681.  
  4682.      virtual   ~Streamable()  {}
  4683.  
  4684.           Standard destructor, virtualized for polymorphic cluster
  4685.           processing.
  4686.  
  4687. ClassRegistry
  4688.  
  4689.  
  4690.      The ClassRegistry class, derived from FunctionRegistry, provides
  4691.      the mechanism for allowing instances of classes derived from
  4692.      Streamable and Mutual to be written out to a stream and later read
  4693.      back into memory from the stream.  There is only one instance of
  4694.      ClassRegistry, namely clasSv.  Each class derived from
  4695.      Streamable/Mutual defines its own register_Class() static member
  4696.      function which registers itself with clasSv.  The following macros
  4697.      make this mechanism transparent to the programmer.
  4698.  
  4699.  
  4700.      #define Register_Class(classType)  \
  4701.                classType::register_Class()
  4702.  
  4703.           You must register your Streamable and Mutual derived
  4704.           classes before they can be streamed.  If you followed the
  4705.           format of pitem.cbk, simply call Register_Class() with your
  4706.           derived class' name as a parameter to register that class.
  4707.  
  4708.      #define Forget_Classes()  clasSv.forget()
  4709.  
  4710.           If you are completely finished with streaming operations for
  4711.           both Streamable and Mutual derived classes you can call
  4712.           Forget_Classes() to free up space held by the
  4713.           ClassRegistry.
  4714.  
  4715. Mutual
  4716.  
  4717.  
  4718. Mutual, derived from Streamable, is a conceptually abstract class that
  4719. additionally provides for mutual ownership behavior in its descendants. 
  4720. Mutual objects are able to record multiple reference counts which can be
  4721. used to avoid accidental deletion.  Even if a Mutual object is multiply
  4722. referenced, any attempt to store the objects referencing it will result in
  4723. only one copy of the Mutual object being stored.  When reloaded from a
  4724. stream, only one copy of the Mutual instance will be reconstructed and
  4725. the multiple links will be automatically reestablished.  The Mutual class
  4726. works in concord with both the ClassRegistry and MutualHoldingPen
  4727. classes.
  4728.  
  4729.  
  4730. Members, private:
  4731.  
  4732.      unsigned refCount;
  4733.  
  4734.           Records the number of "link()s" made to instance. 
  4735.           RefCount is incremented by link() and decremented by
  4736.           unlink().
  4737.  
  4738.      unsigned streamCount;
  4739.  
  4740.           The number of times the instance has been written to the
  4741.           stream during the current streaming operation.  Note that
  4742.           only the first time is the instance written and any additional
  4743.           attempts to write only writes a multiple reference mark. 
  4744.           StreamCount must be less than refCount in order for an
  4745.           instance to be streamed.
  4746.  
  4747.      long streamPos;
  4748.  
  4749.           Once an instance is written to a stream its position on the
  4750.           stream is recorded internally to facilitate the writing of the
  4751.           multiple reference marks on the stream.
  4752.  
  4753.      The following chart shows what happens during a streaming
  4754.      operation.  Notice that if an instance is referenced only once then
  4755.      it will only be streamed once.  The parent pointer is not streamed
  4756.      so in order for this link to persist you must insure that the
  4757.      dominate link, the first link that reaches the instance when storing,
  4758.      is the parent link so that it will be automatically reconstructed
  4759.      when "re-link()ing" the loaded instance.  StreamCount isn't
  4760.      streamed since a reloaded instance's streamCount is always zero. 
  4761.      The user's data resides in descendants of Mutual.  Only on the
  4762.      first encounter with a instance during a streaming operation is it
  4763.      necessary to store the user's data.  The next time only a multiple
  4764.      reference marker is written to the stream.
  4765.  
  4766.  
  4767.           Stream contents:
  4768.  
  4769.                          1st & Only     1st of Many      MultiRef
  4770.  
  4771.           ID()                x              x              0
  4772.           refCount            1              > 1
  4773.           streamCount
  4774.           streamPos                                         x
  4775.           user's data         x              x
  4776.  
  4777.  
  4778.      When the stream is subsequently reloaded, the first time an
  4779.      instance is encountered, the stream registry will load its id.  If its
  4780.      id is non zero then refCount is read and the id is used to look up
  4781.      the Mutual class' descendant's static extract() function, which is
  4782.      called to load the descendant's data. Register_Class() registered
  4783.      this extract() function with the registry.  The descendant's extract()
  4784.      function will call its default constructor which in turn calls
  4785.      Mutual(defaultConstruct) which initializes parenT to NULL and
  4786.      refCount, streamCount, and streamPos to zero.  If the refCount
  4787.      read from the stream is greater than one then the pointer to the
  4788.      loaded Mutual instance returned from the descendant's extract()
  4789.      function is also saved in a holding pen along with the reloaded
  4790.      refCount and the instance's position on the stream.  The pointer to
  4791.      the instance is returned by the overloaded stream extraction
  4792.      operator, i.e. operator>>(istream&,MutuaL&) as usual.
  4793.  
  4794.      If the id is zero then the instance has already been loaded and its
  4795.      pointer is being held in the holding pen of the stream registry.  In
  4796.      this case streamPos is read from the stream's multiple reference
  4797.      mark and used to uniquely identify the instance being held in the
  4798.      holding pen.  Instead of calling the descendant's extract() function,
  4799.      the pointer retrieved from the holding pen is returned by the
  4800.      stream extraction operator.  When the last multiple reference link
  4801.      to a instance held in the holding pen is reconstructed, the holding
  4802.      pen automatically releases the instance.  The holding pen is simply
  4803.      a container.
  4804.  
  4805.      #ifdef MUTUAL_DEBUG
  4806.      static    void mut_error(const char *msg,
  4807.                unsigned id,
  4808.                unsigned refCount,
  4809.                unsigned streamCount,
  4810.                long streamPos);
  4811.      #else
  4812.           #define mut_error(msg,id,refCount,streamCount,streamPos)
  4813.      #endif
  4814.  
  4815.           If you compile pitem.cpp with MUTUAL_DEBUG defined
  4816.           then an error reporting mechanism is put in place on cerr.
  4817.  
  4818.      void init()
  4819.           { refCount = streamCount = 0U; streamPos = 0L; }
  4820.  
  4821.           Called by the Mutual constructors.
  4822.  
  4823. Members, protected:
  4824.  
  4825.      void    assign(const Mutual& m)
  4826.           { Streamable::assign(*(const Streamable*)&m);
  4827.           streamCount = 0U; streamPos = 0L; }
  4828.  
  4829.           Assign() allows for a assign() chain for copy initializer
  4830.           constructors and overloaded assignment operators.  (see
  4831.           Streamable::assign().)
  4832.  
  4833.      Mutual(defaultConstructor) : Streamable(defaultConstruct)
  4834.           { init(); }
  4835.  
  4836.           Insures an unambiguous default constructor for hierarchy
  4837.           chains.  Used when reloading from stream.
  4838.  
  4839.      ostream& insert(ostream& os);
  4840.  
  4841.           Inserts id with refCount or multi-reference mark with
  4842.           streamPos and then calls the inherited Streamable::put()
  4843.           virtual function to store the descendants data if it isn't a
  4844.           multi-reference mark.
  4845.  
  4846.      static    MutuaL extract(istream& is);
  4847.  
  4848.           Acts as a virtual initialize-from-stream constructor. 
  4849.           Mutual::extract() extracts id with refCount from stream if
  4850.           this is the first encounter with the object or the multi-
  4851.           reference mark with streamPos if this is an additional
  4852.           encounter.  On the first encounter, the id is used to look-up
  4853.           the descendant's extract() function which is then called to
  4854.           load the object.  The descendant's extract() first allocates
  4855.           and default constructs the descendant, then calls the
  4856.           descendant's get() function to perform the actual loading of
  4857.           the object.  If refCount is greater than one, a pointer to the
  4858.           reloaded object along with its streamPos is recorded in the
  4859.           holding pen.  When an additional encounter is made on the
  4860.           stream, the streamPos is used to look-up the already loaded
  4861.           objects pointer in the holding pen.  The holding pen also
  4862.           maintains a tally of reconstructed references.  The pointer
  4863.           to the object in the holding pen is returned by
  4864.           Mutual::extract() instead of calling the descendant's
  4865.           extract() again.  When all references to a object in the
  4866.           holding pen have been reconstructed the holding pen
  4867.           destroys the pointer record.  ::Restream() iterates across the
  4868.           holding pen list to determine discrepancies in reconstructing
  4869.           multiple references.
  4870.  
  4871. Members, public:
  4872.  
  4873.      inline ostream& operator<<(ostream& os, Mutual& m)
  4874.           { return m.insert(os); }
  4875.      inline istream& operator>>(istream& is, MutuaL& M)
  4876.           { M = Mutual::extract(is); return is; }
  4877.  
  4878.           These stream operators are actually just friends of Mutual. 
  4879.           Notice how they call insert() and  extract().  Any object
  4880.           derived from a Mutual hierarchy uses these operators for
  4881.           streaming.
  4882.  
  4883.      Mutual() : Streamable()  { init(); }
  4884.  
  4885.           Standard default constructor.
  4886.  
  4887.      virtual  unsigned restream();
  4888.  
  4889.           After a network of Mutual nodes has been saved on stream,
  4890.           restream() must be called before another attempt at saving
  4891.           is made.  This is because streamCount and streamPos must
  4892.           be zeroed.  StreamCount and streamPos are used by the
  4893.           automatic multiple storage mechanism.  StreamCount
  4894.           doesn't allow the instance to be streamed out more times
  4895.           than it has been notified that it is referenced, i.e. refCount
  4896.           via link().  StreamCount also tells the stream registry if this
  4897.           is the first time the instance is being streamed.  If so the
  4898.           descendant's data is streamed out via Streamable::put()
  4899.           after the stream registry outputs the id and refCount.  If not
  4900.           the first time, the multiple reference mark with streamPos
  4901.           is streamed out instead of the id with refCount recording
  4902.           the position on the stream of the actual instance.  After
  4903.           streaming a network of Mutual instances, streamCount
  4904.           should equal refCount for each instance owing to the fact
  4905.           that refCount should be the number of ways an instance can
  4906.           be reached over the network, i.e. the number of link()s. 
  4907.           Mutual::restream() calculates the difference between
  4908.           refCount and streamCount before resetting streamCount and
  4909.           streamPos whenever streamPos is non zero.  If pitem.cpp
  4910.           was compiled with MUTUAL_DEBUG defined the
  4911.           discrepancy is reported on cerr.  If streamPos is zero,
  4912.           restream() returns zero since no streaming has taken place
  4913.           since the last restream() or constructor call.  (See also
  4914.           Mutual::extract() above.)
  4915.  
  4916.      void     unlink(void * P = (void *)0);
  4917.  
  4918.           Decrements refCount if non zero, returning its value.  If
  4919.           pitem.cpp was compiled with MUTUAL_DEBUG defined
  4920.           any underflow is reported on cerr.  If P is equal to parenT
  4921.           then parenT is reset to NULL.  Unlink() is called by cl::
  4922.           detachD() which in turn is called by container unbinding
  4923.           methods, e.g. atDel(), atPut(), and atXchg(), for automatic
  4924.           unlinking of Mutual nodes.
  4925.  
  4926.      int      link(void * P = (void *)0);
  4927.  
  4928.           Increments refCount if less than UINT_MAX to indicate
  4929.           additional references in memory to the instance.  Overflow
  4930.           errors are reported on cerr if pitem.cpp was compiled with
  4931.           MUTUAL_DEBUG defined.  If P is not NULL and parenT
  4932.           is, P is assigned to parenT.  Link() is called by cl::
  4933.           attachD() which in turn is called by container binding
  4934.           methods, e.g. atIns(), atGet(), and atXchg(), for automatic
  4935.           linking of Mutual nodes.
  4936.  
  4937.      unsigned RefCount()  { return refCount; }
  4938.  
  4939.           Called by cl:: deleteD() to see if a Mutual node can be
  4940.           safely deleted.  Only when zero is returned is it okay to
  4941.           delete a Mutual node.  See link() and unlink().
  4942.  
  4943.      long     StreamPos()  { return streamPos; }
  4944.  
  4945.           Provided for building Mutual object file indexes.  After
  4946.           streaming out and before restream() is called, call
  4947.           StreamPos() to determine the position of the Mutual object
  4948.           in the file.
  4949.  
  4950.      virtual ~Mutual()  {}
  4951.  
  4952.           Provided so that descendants can be virtually destroyed.
  4953.  
  4954.  
  4955. MutualHoldingPen
  4956.  
  4957.  
  4958.      The MutualHoldingPen class provides the mechanism for allowing
  4959.      instances of classes derived from Mutual to be read back into
  4960.      memory from the stream automatically reconstructing the multiple
  4961.      references.  There is only one instance of MutualHoldingPen,
  4962.      namely instancEv.  The following macro makes this mechanism
  4963.      transparent to the programmer.
  4964.  
  4965.  
  4966.      #define Restream() instancEv.restream()
  4967.  
  4968.           The Restream() macro expands into a call to the restream()
  4969.           member function without the need for a instance name. 
  4970.           When Mutual instances are reloaded from a stream back
  4971.           into memory, instances with multiple references are loaded
  4972.           only once while pointers to these instances are held in a
  4973.           holding pen within the instancEv so that the multiple
  4974.           references can be reconstructed as they are encountered. 
  4975.           Once the stream is fully loaded all instances should have
  4976.           already been automatically released from the holding pen. 
  4977.           Restream() releases any stragglers, reporting the relinking
  4978.           discrepancies on cerr if pitem.cpp was compiled with
  4979.           MUTUAL_DEBUG defined.  You should call Restream()
  4980.           after every reloading operation to insure that holding pen is
  4981.           clear.  Restream() returns the number of objects with
  4982.           discrepancies.
  4983.  
  4984.  
  4985.  
  4986.                                                                       Index
  4987.  
  4988.  
  4989.  
  4990. ~cl() 76
  4991. ~Mutual() 104
  4992. ~Streamable() 98
  4993.  
  4994. A
  4995. allClr() 80
  4996. allDel() 70, 80
  4997. allRmv() 70, 79
  4998. ANSI C 8
  4999. ANSI string class 24
  5000. Asg suffix 70
  5001. Assign 12
  5002. assign() 47, 50, 70, 95, 102
  5003. assignD() 48, 50, 70, 71
  5004. Assignment operator 50
  5005. atDel() 79
  5006. atDelAsg() 79
  5007. atGet() 80
  5008. atGetAsg() 80
  5009. atIns() 79
  5010. atInsNew() 79
  5011. atPut() 80
  5012. atPutAsg() 80
  5013. atPutNew() 80
  5014. atRmv() 79
  5015. attachD() 49, 72, 97, 104
  5016. atXchg() 81
  5017.  
  5018. B
  5019. Bags 22
  5020.  
  5021. C
  5022. catch_reset() 9
  5023. Circle 53
  5024. cl (void ** argv 75
  5025. cl() 74
  5026. cl(defaultConstructor) 70
  5027. CL_ALL_DEF 45
  5028. CL_ASG 17, 33, 48, 70
  5029. CL_ASSIGN_BYTES 41
  5030. CL_ASSIGN_DEF 45
  5031. CL_ASSIGN_OP 32, 48
  5032. CL_assignD() 45, 56, 70
  5033. CL_BIND_ONLY 70
  5034. CL_CDECL 8
  5035. CL_char 39
  5036. CL_CLONE_BYTES 41
  5037. CL_CMP_BYTES 41
  5038. CL_CMP_DEF 45
  5039. CL_cmpD() 45
  5040. CL_COPYINIT 33
  5041. CL_DEL 17, 70
  5042. CL_DELETE_DEF 46
  5043. CL_deleteD() 46
  5044. CL_DELTA 75
  5045. CL_EQ_GT_OPS 32
  5046. CL_getD() 46
  5047. CL_LIMIT 75
  5048. CL_MAXNODES 75
  5049. CL_NEW 17, 34, 48, 70
  5050. CL_NEW_DEF 45
  5051. CL_newD() 45, 56
  5052. CL_NEWED 70
  5053. CL_NO_EXCEPTIONS 8
  5054. CL_NO_STDARG_H 8
  5055. CL_NO_TEMPLATES 8
  5056. CL_NO_THROW_XALLOC 8
  5057. CL_NOTFOUND 21, 86
  5058. CL_PITEM() 56
  5059. CL_putD() 46
  5060. CL_READ 70
  5061. CL_READ_DEL 70
  5062. CL_SAVE 17, 35, 70
  5063. CL_SORTED 70
  5064. CL_STRM_BYTES 41
  5065. CL_STRM_EXTRACT_DEF 46
  5066. CL_STRM_INSERT_DEF 46
  5067. CL_STRM_OPS 34
  5068. CL_THROW_XALLOC 8
  5069. CL_WELL_ENDOWED 35
  5070. Class level defaults 50
  5071. Class level members 56
  5072. ClassRegistry 99
  5073. CLcmPcast() 21, 48
  5074. clexamps.cpp 7
  5075. cllib.src 7
  5076. Clone 12
  5077. clone() 56, 97
  5078. cmP 70
  5079. cmPD() 48, 71
  5080. Compare function 21
  5081. Copy initializer 33, 37, 50
  5082. curNode 69
  5083. CurNode() 86
  5084. Current node 18
  5085.  
  5086. D
  5087. Default constructor 34, 37
  5088. defaultConstructor 47
  5089. Del suffix 70
  5090. del() 86
  5091. DelAsg suffix 70
  5092. delAsg() 86
  5093. Delete 12
  5094. deleteD() 49, 50, 72, 104
  5095. delta 69
  5096. Delta() 77
  5097. Deque 16
  5098. destruct() 47, 50, 71
  5099. Destructor 37, 50
  5100. detachD() 49, 73, 97, 103
  5101. Dictionaries 22
  5102. Doubly linked list 18
  5103. Dynamic nodes 18
  5104.  
  5105. E
  5106. Elastic array 11
  5107. examp101.cpp 9
  5108. examp201.cpp 12
  5109. examp202.cpp 14
  5110. examp203.cpp 15
  5111. examp204.cpp 17
  5112. examp205.cpp 18
  5113. examp206.cpp 20
  5114. examp207.cpp 22
  5115. examp208.cpp 24
  5116. examp209.cpp 24
  5117. examp210.cpp 24
  5118. examp211.cpp 25
  5119. examp301.cpp 37
  5120. examp302.cpp 40
  5121. examp303.cpp 43
  5122. examp401.cpp 47
  5123. examp501.cpp 51
  5124. examp502.cpp 53
  5125. examp503.cpp 57
  5126. examp504.cpp 60
  5127. examp505.cpp 64
  5128. examp601.cpp 78
  5129. examp602.cpp 93
  5130. Examples subdirectory 7
  5131. extract() 56, 96, 102
  5132.  
  5133. F
  5134. FIFO 16
  5135. findAll() 92
  5136. findLast() 91
  5137. findNext() 91
  5138. findPrev() 91
  5139. first 69
  5140. flags 70
  5141. Flags() 77
  5142. fnC2ID() 93
  5143. FOLO 16
  5144. forEach() 64, 81
  5145. Forget_Classes() 99
  5146. Forget_Functions() 93
  5147. Form templates 29
  5148. FunctionRegistry 93
  5149.  
  5150. G
  5151. gconsole.cpp 51
  5152. Geometric shapes 51
  5153. get() 49, 73, 87, 95
  5154. getD() 49, 53, 73
  5155. Granularity 11
  5156. Graphic segments 64
  5157.  
  5158. H
  5159. Heterogeneous data 51
  5160. Hysteresis 11
  5161.  
  5162. I
  5163. ID() 56, 97
  5164. ID2fnC() 93
  5165. index() 81
  5166. init() 69, 95, 102
  5167. ins() 86
  5168. insert() 96, 102
  5169. insNew() 86
  5170. insQ() 84
  5171. insQNew() 85
  5172. Installation 7
  5173. iomanip.h 16
  5174. iostream.h 16
  5175. ITEM_char 39
  5176.  
  5177. L
  5178. level() 97
  5179. Library building 7
  5180. LIFO 16
  5181. limit 69
  5182. Limit() 76
  5183. link() 97, 103
  5184. linkS 69
  5185. List 18
  5186. load() 74
  5187. Logical array 69
  5188. lowLimit 69
  5189. lowTheshold 69
  5190.  
  5191. M
  5192. makefile 7
  5193. maxNodes 69
  5194. MaxNodes() 77
  5195. Mediator functions 43
  5196. MS Windows 24
  5197. Multi-faceted hybrid behavior 11
  5198. Multiple reference arbitration 51
  5199. Multiple reference resolution 68
  5200. Mutual 51, 64, 100
  5201. Mutual holding pen 68
  5202. Mutual() 103
  5203. Mutual(defaultConstructor) 102
  5204. MUTUAL_DEBUG 68, 101
  5205. MutualHoldingPen 105
  5206.  
  5207. N
  5208. Network 68
  5209. New suffix 70
  5210. newD() 48, 70, 72
  5211. nodes 69
  5212. Nodes() 77
  5213.  
  5214. O
  5215. Object file indices 104
  5216. operator TYPE *() 87
  5217. operator unsigned() 97
  5218. operator<<() 34, 37, 78, 85, 96, 103
  5219. operator=() 32, 37, 75, 97
  5220. operator==() 31, 36, 75
  5221. operator>() 31, 36, 75
  5222. operator>>() 34, 37, 84, 96, 103
  5223. operator[]() 80
  5224.  
  5225. P
  5226. pack() 76
  5227. parenT 95
  5228. ParenT() 97
  5229. Physical array 69
  5230. PieSlice 64
  5231. pitem.cbk 51
  5232. PITEM_STRM_BASE 57
  5233. Polymorphic clusters 51
  5234. pop() 84
  5235. popDel() 84
  5236. popDelAsg() 84
  5237. Protected scope virtual functions 47
  5238. push() 84
  5239. pushNew() 84
  5240. put() 49, 73, 87, 95
  5241. putAsg() 87
  5242. putD() 49, 53, 73
  5243. putNew() 87
  5244.  
  5245. Q
  5246. Queue 16
  5247.  
  5248. R
  5249. rear() 85
  5250. rearAsg() 85
  5251. Rectangle 56
  5252. refCount 100
  5253. RefCount() 98, 104
  5254. Register_Class() 56, 99
  5255. Register_CmP() 93
  5256. Register_Function() 93
  5257. resetFlags() 78
  5258. restream() 68, 96, 103, 105
  5259. rmv() 86
  5260.  
  5261. S
  5262. save() 70, 74
  5263. Search 21
  5264. SegRef 64
  5265. setCurNode() 86
  5266. setDelta() 77
  5267. setFlags() 77
  5268. setLimit() 76
  5269. setMaxNodes() 77
  5270. Sets 22
  5271. Shapes 53
  5272. Sort 21
  5273. Stack 16
  5274. Static nodes 18
  5275. stdarg.h 8
  5276. Stream contents 101
  5277. Streamable 51
  5278. Streamable() 96
  5279. Streamable(defaultConstructor) 95
  5280. streamCount 68, 100
  5281. streamPos 68, 100
  5282. StreamPos() 104
  5283. string.h 16
  5284.  
  5285. T
  5286. top() 84
  5287. topAsg() 84
  5288. Typeless container 20, 29
  5289.  
  5290. U
  5291. UNIX V 8
  5292. unlink() 97, 103
  5293. unQ() 85
  5294. unQDel() 85
  5295. unQDelAsg() 85
  5296.  
  5297. V
  5298. vacancy() 77
  5299. vacancyNonElastic() 77
  5300. Variable argument lists 64
  5301. vector() 76
  5302. vforEach() 74
  5303. Virtual destructor 50
  5304. Virtual initialize-from-stream constructor
  5305.           56
  5306. Virtual put() 56
  5307. voidApplY 81
  5308.  
  5309. W
  5310. Wrapper class 12
  5311.