home *** CD-ROM | disk | FTP | other *** search
/ Shareware Supreme Volume 6 #1 / swsii.zip / swsii / 099 / CL181.ZIP / CL.DOC < prev    next >
Text File  |  1993-04-26  |  34KB  |  1,011 lines

  1. Container Lite (CL version 1.81:  4/26/93)
  2. A Portable Persistent Hybrid Container Class for C++
  3. Copyright 1993, John Webster Small, All rights reserved
  4. PSW / Power SoftWare, P.O. Box 10072, McLean, VA 22102
  5. (703) 759-3838
  6.  
  7. This version of Container Lite is licensed to you as shareware.  For 
  8. details, see section 7 below.
  9.  
  10.  
  11. 1.0  Introduction
  12.  
  13. Applications using Container Lite typically use 80% less container 
  14. code than those coded with other commercial and compiler bundled 
  15. container class libraries.  Use Container Lite repeatedly throughout 
  16. all your applications for even greater savings in design and coding 
  17. effort.  Container Lite is ideal for coding games, rolling your own 
  18. application frameworks, and any application requiring user 
  19. configuration persistence.
  20.  
  21. Container Lite is a portable, persistent, hybrid container class 
  22. library compatible with compilers supporting AT&T's C++ 2.x, 3.x 
  23. standard.  Container Lite is based upon a single container class 
  24. having a hybrid stack, queue, deque, list, array interface and 
  25. built-in sort, search, and iterate member functions.
  26.  
  27.  
  28. 1.1  Implementation
  29.  
  30. Container Lite is implemented as a dynamically sized array of void 
  31. pointers, pointing to the data you are collecting.  Template 
  32. invocations additionally define primitives for on the fly copy 
  33. initialization, assignment, and destruction of your data nodes 
  34. automatically.  Container Lite's "form template" provides the same 
  35. automatic code generation and strong type checking for compilers not 
  36. yet supporting templates.
  37.  
  38.  
  39. 1.2  Installation
  40.  
  41. Copy the following Container Lite files to the appropriate development 
  42. directory for your C++ compiler if you have not already done so.
  43.  
  44.     cl.doc        this file
  45.     cl.hpp        Container Lite header
  46.     cl.cpp        Container Lite source
  47.  
  48.     cl.hpt        Container Lite template
  49.     cl.hpf        Container Lite form template
  50.  
  51.  
  52. 2.0  Hybrid Container
  53.  
  54. Always think of Container Lite in terms of its multi-faceted hybrid 
  55. behavior.  One moment your container is a stack, the next a priority 
  56. queue or array, etcetera.  With Container Lite you are never locked 
  57. into some arcane branch class of a convoluted, towering hierarchy like 
  58. you would be with a conventional "textbook" container implementation.
  59.  
  60.  
  61. 2.1  Dynamic Array
  62.  
  63. Container Lite's dynamic array behavior provides the foundation for 
  64. the rest of its inherent behaviors.  Imagine an array in which you can 
  65. insert or extract cells at any location.  Container Lite lets you 
  66. specify the granularity and hysteresis of this elasticity in 
  67. constructor and/or housekeeping calls with a delta parameter.  In 
  68. other words, when a new cell needs to be added how many additional 
  69. spare cells should be provided to optimize future expansion efforts?  
  70. And how many spare cells should be allowed to accumulate before 
  71. compaction?  The following list of member functions catalogs this 
  72. dynamic array behavior.
  73.  
  74.     vector()    Limit()        setLimit()
  75.     pack()        Delta()        setDelta()
  76.     Nodes()        MaxNodes()    setMaxNodes()
  77.     vacancy()    vacancyNonElastic()
  78.     atIns()        atInsNew()    atRmv()
  79.     allRmv()    atDel()        atDelAsg()
  80.     allDel()    atPut()        atPutNew()
  81.     atPutAsg()    atGet()        operator[]()
  82.     atGetAsg()    atXchg()    index()
  83.     forEach()
  84.  
  85. If you use the Container Lite template to generate a strongly typed 
  86. check wrapper class for your data, you can then use the above 
  87. primitives with "Asg", "New", and "Del" suffixes.  These primitives 
  88. allow you to assign, clone, and delete your data nodes on the fly 
  89. which we will see in our first example.
  90.  
  91.     // examp211.cpp -- link with cl.obj
  92.  
  93.     #include <stdlib.h>    // rand(), srand()
  94.     #include <time.h>    // time_t, time()
  95.     #include "cl.hpt"
  96.  
  97.     main()
  98.     {
  99.       time_t t;
  100.  
  101.       srand((unsigned) time(&t));
  102.       CL<int> c(CL_DANDS,15);
  103.       int i = 0;
  104.       while (c.atInsNew(rand()%(c.Nodes()+1),&i))
  105.         i++;
  106.       cout << "0-14 in random order: " << endl;
  107.       while (c.atDelAsg(0,&i))
  108.         cout << i << endl;
  109.       return 0;
  110.     }
  111.  
  112. CL<int> generates a wrapper class for integers.  The CL_DANDS flag 
  113. enables the container's "Asg", "New", "Del" and stream (S)torage 
  114. primitives.  The numbers zero through fourteen are inserted at random 
  115. locations in the "array."  AtInsNew() performs the same operation as 
  116. atIns() except the data is cloned on the fly and the clone is bound in 
  117. the container instead of the variable i.  AtDelAsg() basically 
  118. performs an atGetAsg(), (As)si(g)ning (copying) the outgoing node to 
  119. the variable i, before performing an equivalent atDel() which is the 
  120. same thing as an atRmv() except the outgoing node is (Del)eted and not 
  121. just the cell (R)e(m)o(v)ed from the array.
  122.  
  123. If your compiler doesn't support templates you can use the "form 
  124. template" approach shown below instead.
  125.  
  126.     // examp212.cpp -- link with cl.obj
  127.  
  128.     #include <stdlib.h>    // rand(), srand()
  129.     #include <time.h>    // time_t, time()
  130.  
  131.     #define   ITEM int
  132.     #define   CL CLint
  133.     #include "cl.hpf"
  134.  
  135.     main()
  136.     {
  137.       time_t t;
  138.       srand((unsigned) time(&t));
  139.       CLint c(CL_DANDS,15);
  140.       int i = 0;
  141.       while (c.atInsNew(rand()%(c.Nodes()+1),&i))
  142.         i++;
  143.       cout << "0-14 in random order: " << endl;
  144.       while (c.atDelAsg(0,&i))
  145.         cout << i << endl;
  146.       return 0;
  147.     }
  148.  
  149. Notice that you must define ITEM as the parameter type that would 
  150.  
  151. normally be passed as the template parameter, i.e. "#define ITEM int" 
  152. replaces "CL<int>".  Likewise CL is defined as new type name so that 
  153. you use "CLint" in place of "CL<int>".
  154.  
  155. The manual that comes with the registered version of Container Lite 
  156. includes, among other things, a reference section detailing all 
  157. primitive operations.  For now you will need to refer to cl.hpp 
  158. declarations and infer the operation of each member function from what 
  159. you glean from these examples.  Also if you want to run these examples 
  160. yourself, you must use your ascii editor to extract them to their own 
  161. files.  I didn't include them separately in order to reduce the 
  162. download size of this shareware version.
  163.  
  164.  
  165. 2.2  Stack-Queue-Deque
  166.  
  167. A stack provides LIFO (Last In, First Out) storage.  Container Lite 
  168. has a complete set of stack primitives.  These functions don't disturb 
  169. the list behavior of a container, e.g. current node setting.  A queue 
  170. provides FIFO (First In, First Out) storage.  And the deque 
  171. (pronounced deck) provides FOLO (First Out, Last Out) storage.  Think 
  172. of the deque as a combination stack-queue with the additional 
  173. capability of being able to be popped from the rear.  The following is 
  174. a list of the member functions supporting this stack-queue-deque 
  175. behavior:
  176.  
  177.     push()        pushNew()    pop()
  178.     popDel()    popDelAsg()    top()
  179.     topAsg()    insQ()        insQNew()
  180.     unQ()        unQDel()    unQDelAsg()
  181.     rear()        rearAsg()    operator<<()
  182.     operator>>()
  183.  
  184. The example for this section uses a forms generated container of 
  185. floats.
  186.  
  187.     // examp221.cpp -- link with cl.obj
  188.  
  189.     #define   ITEM  float
  190.     #define   CL    CLfloat
  191.     #include "cl.hpf"
  192.  
  193.     main()
  194.     {
  195.       CLfloat f(CL_DASSIGN|CL_DNEW|CL_DDELETE,10);
  196.       for (float i = 1.0; f.pushNew(&i); i++);
  197.       cout << "Count to 10: " << endl;
  198.       while (f.unQDelAsg(&i))
  199.         cout << i << endl;
  200.       return 0;
  201.     }
  202.  
  203. We can see individual flags being set in the constructor call this 
  204. time.  Without CL_DNEW being set, pushNew() would have been inhibited 
  205. and all other member functions with the "New" suffix.  Without 
  206. CL_DASSIGN and CL_DDELETE being set, unQDelAsg() would also have been 
  207. inhibited.  This container is limited to a maximum of 10 items.  If 
  208. you look in cl.hpf on line 238 you will see that the constructor 
  209. parameter named "maxNodes" is passed the actual parameter of 10.  This 
  210. can be changed at any time with a call to setMaxNodes() that you saw 
  211. cataloged in the previous section.  The unQ() family of member 
  212. functions work on the rear of the queue thus providing deque behavior.
  213.  
  214. The container's destructor is automatically called just before "return 
  215. 0;".  Since the previous while loop has already deleted all nodes 
  216. there is nothing left to delete.  However, you should take note that 
  217. if a container's CL_DDELETE flag is raised the destructor will attempt 
  218. to delete any remaining nodes, otherwise the nodes are simply removed.  
  219. Thus you should typically segregate you containers into two groups: 
  220. those with static nodes and those with dynamic.  You don't want to 
  221. delete something that isn't dynamically allocated!  You can mix the 
  222. two types but it's safest not to set the CL_DDELETE flag since a 
  223. compiler generated destructor call could then jump out and bite you!
  224.  
  225.  
  226. 2.3  List
  227.  
  228. Any container can be treated as if it were a doubly linked list 
  229. thereby providing sequential storage.  In a container's header 
  230. structure a current node index is maintained that lets the list 
  231. primitives know where the insertion or removal is to take place.  The 
  232. dynamic array and stack-queue-list primitives don't disturb this 
  233. current node index unless of course it points to the cell that's being 
  234. removed.  In that case the current node becomes undefined just like it 
  235. was when the container was first constructed.  Here's a list of the 
  236. list primitives.
  237.  
  238.     CurNode()    setCurNode()    ins()
  239.     insNew()    rmv()        del()
  240.     delAsg()    put()        putNew()
  241.     putAsg()    get()        getAsg()
  242.     next()        operator++()    nextAsg()
  243.     prev()        operator--()    prevAsg()
  244.  
  245.  
  246. The example for this section contrasts the template and "form 
  247. template" approaches.
  248.  
  249.     // examp231.cpp -- link with cl.obj
  250.     // #define CPP_TEMPLATES
  251.  
  252.     #if defined(CPP_TEMPLATES)
  253.       #include "cl.hpt"
  254.       ITEM_DEL_ONLY(char)
  255.       #define CLstr CL<char>
  256.     #else
  257.       #define   ITEM char
  258.       #define   ITEM_DEL_ONLY
  259.       #define   CL CLstr
  260.       #include "cl.hpf"
  261.     #endif    
  262.  
  263.     main()
  264.     {
  265.       CLstr s;
  266.       s.ins("C++ strings are ");
  267.       s.ins("not as well ");
  268.       s.ins("behaved as ");
  269.       // s.CurNode() == 2
  270.       s.insNew("This string won't appear!");
  271.       s << "they should be!";
  272.       s.setCurNode();
  273.       // s.CurNode == CL_NOTFOUND != 0
  274.       while (++s)
  275.         cout << (char *)s << endl;
  276.       return 0;
  277.     }
  278.  
  279. If you compiler supports templates you have the option of uncommenting 
  280. the second line "#define CPP_TEMPLATES."  The ITEM_DEL_ONLY macro 
  281. turns off the "Asg", "New", and stream storage primitives.  This is 
  282. necessary since the template instantiation can't correctly infer a 
  283. meaningful default copy initializer or assignment operator for C++ 
  284. strings.  Though stream insertion is possible it is precluded since a 
  285. default constructor for strings is required for stream extraction.  We 
  286. are left with only being able to generate a string deletion method.
  287.  
  288. However, the string container is constructed in main() via a default 
  289. constructor.  If you look in cl.hpp, cl.hpt, or cl.hpf, you will 
  290. notice that the default constructor sets the container's flags to 
  291. CL_BIND_ONLY.  Thus "Del" primitives are inhibited at this point even 
  292. though the (form) template has provided for string deletion.  This is 
  293. okay since all the strings bound in the container are statically 
  294. allocated and if we accidently call a "Del" primitive it will return 
  295. fail instead of crashing the program!
  296.  
  297. The nodes of a container are indexed just like an array, zero to the 
  298. number of nodes minus one.  Hence the third node has an index of two.  
  299. Calling setCurNode() with the default parameter resets the list's 
  300. current to undefined, i.e. CL_NOTFOUND.  When the list iterator is 
  301. subsequently applied, since no node is current to begin with, the 
  302. current node is set to zero which happens to be the first node in the 
  303. list.  The list iterator returns a pointer to the current node.  Upon 
  304. reaching the end of the list the current node is reset and the list 
  305. iterator returns the NULL pointer forcing the while loop to terminate.  
  306. (If the iterator were called again at this point, the current node 
  307. would wrap around to the front and the process would begin anew.)
  308.  
  309. Since containers are persistent, ambiguity exists when attempting to 
  310. insert a container into a stream.  Did you want to insert the current 
  311. node or the whole container.  In our example the explicit type cast 
  312. (char *) resolves this ambiguity and calls the implicit type cast 
  313. operator returning a pointer to the current node which happens to be a 
  314. string.
  315.  
  316. Other than type checking for strings, the template instantiations this 
  317. time gain us nothing in the way of automatic code generation.  We 
  318. could have just as easily used a typeless container which the next 
  319. example demonstrates.
  320.  
  321.     // examp232.cpp -- link with cl.obj
  322.  
  323.     #include "cl.hpp"
  324.  
  325.     main()
  326.     {
  327.       cl s;
  328.       s.ins("C++ strings are ");
  329.       s.ins("not as well ");
  330.       s.ins("behaved as ");
  331.       // s.CurNode() == 2
  332.       s.insNew("This string won't appear!");
  333.       s << "they should be!";
  334.       s.setCurNode();
  335.       // s.CurNode == CL_NOTFOUND != 0
  336.       while (++s)
  337.         cout << (char *)s.get() << endl;
  338.       return 0;
  339.     }
  340.  
  341. Notice that "cl" is a typeless container where as CL was used with 
  342. templates.  Since the type is unknown an implicit type cast operator 
  343. is unavailable (the template generates this operator).  Many times 
  344. when working with non persistent heterogeneous data it is easiest to 
  345. play fast and loose with a typeless container.
  346.  
  347.  
  348. 2.4  Sort-Search-Unique
  349.  
  350. Containers can also be sorted and searched on a default or user 
  351. supplied compare function.  You don't have to derive a new class for 
  352. each different sort order!  Only template and "form template" 
  353. generated containers have default compare functions (only if the 
  354. template parameter type has implicitly or explicitly defined 
  355. relational operators).  Please note that these primitives affect the 
  356. list's current node setting.
  357.  
  358.     Sorted()    UnSort()    setCmP()
  359.     CmP()        sort()        insSort()
  360.     insSortNew()    insUnique()    insUniqueNew()
  361.     findFirst()    findNext()    findLast()
  362.     findPrev()    findAll()
  363.  
  364. With these primitives you can build bags, sets, dictionaries, etc.. In 
  365. our next example we will rely on the (form) template to generate 
  366. default copy initialization, assignment, and destruction code since 
  367. these are not declared or defined for the employee structure.
  368.  
  369.     // examp241.cpp -- link with cl.obj
  370.     // #define CPP_TEMPLATES
  371.  
  372.     #include <string.h>
  373.  
  374.     struct employee  {
  375.       char name[20];
  376.       unsigned salary;
  377.       employee *operator()
  378.         (const char *name, unsigned salary)
  379.       {
  380.         strncpy(this->name,name,20);
  381.         this->salary = salary;
  382.         return this;
  383.       }
  384.       employee(const char * name, unsigned salary)
  385.       {  (void) (*this)(name,salary); }
  386.       static int nameCmp(const employee * E1,
  387.         const employee * E2)
  388.       { return strcmp(E1->name,E2->name); }
  389.       static int salaryCmp(const employee * E1,
  390.         const employee * E2)
  391.       { return (int)(((long)(E1->salary))
  392.         -(long)(E2->salary)); }
  393.     };
  394.  
  395.     #if defined(CPP_TEMPLATES)
  396.         #include "cl.hpt"
  397.         ITEM_NO_REL_STRM_OPS(employee)
  398.         #define CLe CL<employee>
  399.     #else
  400.         #define ITEM employee
  401.         #define ITEM_NO_REL_STRM_OPS
  402.         #define CL CLe
  403.         #include "cl.hpf"
  404.     #endif
  405.  
  406.     main()
  407.     {
  408.       CLe ce(CL_DNEW|CL_DDELETE);
  409.       ce.setCmP(employee::salaryCmp);
  410.       ce.insSort(new employee("John Doe",1000));
  411.       employee e("John Small",100);
  412.       ce.insSort(new employee(e));
  413.       ce.insSortNew(e("Sally Good",10000));
  414.       cout << "Employees in increasing salary order: "
  415.         << endl;
  416.       for (ce.setCurNode(0); ce; ++ce)
  417.         cout << "\nEmployee name: "
  418.           << ((employee *)ce)->name << endl
  419.           << "  salary: "
  420.           << ce.get()->salary << endl;
  421.       ce.setCmP(employee::nameCmp);
  422.       cout << "\n\nFind employee: "
  423.         << e.name << endl;
  424.       if (ce.findFirst(&e))
  425.         cout << "\nEmployee name: "
  426.           << ((employee *)ce)->name << endl
  427.           << "  salary: "
  428.           << ce.get()->salary << endl;
  429.       else
  430.         cout << "\nnot found" << endl;
  431.       return 0;
  432.     }
  433.  
  434. The ITEM_NO_REL_STRM_OPS macro is used to turn off (form) template 
  435. generation of compare and stream operations since the employee 
  436. structure doesn't overload relational or stream operators.  Thus even 
  437. if the constructor call raised the CL_DSTORE flag the only thing that 
  438. would be stored on stream for the container would be its header 
  439. information but not the nodes.
  440.  
  441. This example performs an insertion sort on increasing salary order of 
  442. three dynamically allocated employee records using various techniques 
  443. to test your C++ acumen.  The sort order was determined by the 
  444. previous call to setCmP().  Notice the for loop initializes the 
  445. current node to zero, the first node in the list.  The for loop's test 
  446. expression calls the implicit type cast operator, generated by the 
  447. (form) template, which returns a pointer to the current node.  The for 
  448. loop's reinitialization statement calls the container's list iterator.
  449.  
  450. Now setCmP() is called again to key in on names.  If sort() were 
  451. called next, the list would be sorted in alphabetical order.  Instead 
  452. findFirst() is used to find the matching name.  The container is able 
  453. to determine if it is still in sorted order since the last sort.  If 
  454. it is, findFirst() will use a binary search algorithm otherwise a 
  455. linear search will be performed.  FindNext() always uses a linear 
  456. search; however, if the list is in sorted order only the next node is 
  457. tested for a match and the result returned.  If the list is not sorted 
  458. then the search for a match will continue throughout the remainder of 
  459. the list.  Once a container is sorted, it will remain in a sorted 
  460. state, as far as the container is concerned, until the compare 
  461. function is changed or a new node is added without insSort???() or 
  462. insUnique???().  However, it is possible for you to access a node, 
  463. modifying the key value, without the container being aware that the 
  464. sorted order has been spoiled.  Be sure to use UnSort() to let the 
  465. container know if it is no longer sorted in these cases.
  466.  
  467.  
  468. 3.0  Strong Type Checking
  469.  
  470. Container Lite's foundation class is named "cl".  This class used by 
  471. itself performs no type checking on the data being bound.  These 
  472. typeless containers are useful for quick and dirty handling of 
  473. heterogeneous data types.  However, by using a template a container 
  474. can be made to impose strong type checking for any type of data at 
  475. compile time, thereby restricting the use of the container to a 
  476. particular data type.  Even if your compiler doesn't support 
  477. templates, strong type checking can still be achieved with Container 
  478. Lite's "form templates."  You have already seen all three approaches 
  479. in the examples of the previous section.
  480.  
  481.  
  482. 3.1  A Well Endowed Data Type
  483.  
  484. A fully functional container requires its nodes' data type to be well 
  485. endowed or more precisely that the nodes' data type (ITEM) must have 
  486. (either implicitly or explicitly defined):
  487.  
  488.     1. an overloaded equality operator, i.e.
  489.  
  490.         int ITEM::operator==(const ITEM&) const;
  491.        or
  492.         int operator==(const ITEM&, const ITEM&); 
  493.                 //friend
  494.  
  495.        and an overloaded greater than operator, i.e.
  496.  
  497.         int ITEM::operator>(const ITEM&) const;
  498.        or
  499.         int operator>(const ITEM&, const ITEM&);
  500.  
  501.     2. a copy initializer constructor, i.e.
  502.  
  503.         ITEM::ITEM(const ITEM&);
  504.  
  505.     3, an overloaded assignment operator, i.e.
  506.  
  507.         ITEM& ITEM::operator=(const ITEM&);
  508.  
  509.     4. an overloaded stream insertion operator, i.e.
  510.  
  511.         ostream& operator<<(ostream&,ITEM&);
  512.  
  513.     5. an overloaded stream extraction operator, i.e.
  514.  
  515.         istream& operator>>(istream&,ITEM&);
  516.  
  517.        and a default constructor, i.e.
  518.  
  519.         ITEM::ITEM();
  520.  
  521.     and
  522.  
  523.     6. a destructor if one is required, i.e.
  524.  
  525.         ITEM::~ITEM();
  526.  
  527. Overloaded relational operators, and stream operators can't be 
  528. inferred implicitly by the compiler for non native types.  In 
  529. examp241, the employee structure lacked both the requisite relational 
  530. and stream operators.  Hence the ITEM_NO_REL_STRM_OPS macro was 
  531. employed in conjunction with the (form) template.  The macros 
  532. ITEM_NO_REL_OPS, ITEM_NO_STRM_INSERT, and ITEM_NO_STRM_EXTRACT could 
  533. have been used individually instead for more precise (form) template 
  534. tuning.
  535.  
  536. The compiler can supply a default copy initializer constructor and 
  537. assignment operator which is what examp241 relied on.  Sometimes these 
  538. defaults are unacceptable as they were in examp231 and only default 
  539. deletion was okay.  Thus examp231 used the ITEM_DEL_ONLY macro.  The 
  540. only way to avoid default deletion is to leave a container's 
  541. CL_DDELETE flag reset.  If you look cl.hpt you will see that 
  542. ITEM_DEL_ONLY is defined as:
  543.  
  544.     #define ITEM_DEL_ONLY(ITEM)        \
  545.         ITEM_NO_REL_OPS(ITEM)        \
  546.         ITEM_NO_ASSIGN(ITEM)        \
  547.         ITEM_NO_COPYINIT(ITEM)        \
  548.         ITEM_NO_STRM_INSERT(ITEM)    \
  549.         ITEM_NO_STRM_EXTRACT(ITEM)
  550.  
  551. Of course you can fine tune a (form) template instantiation by 
  552. invoking only those macros that accurately define the deficiencies of 
  553. you data type.
  554.  
  555. Let's now look at a well endowed (as far as Container Lite's template 
  556. facility is concerned) string class that will yield us a fully 
  557. functional container of strings.
  558.  
  559.     // examp311.cpp -- link with cl.obj
  560.  
  561.     #include <string.h>
  562.     #include <iostream.h>
  563.  
  564.     class String {
  565.  
  566.         char *str;
  567.         size_t len;
  568.         friend ostream& operator<<
  569.             (ostream& os, String& s);
  570.         friend istream& operator>>
  571.             (istream& is, String& s);
  572.         int    cmp(const String& cs) const;
  573.     public:
  574.  
  575.         static char safety;
  576.         static size_t cmpn;
  577.         static int cmpi;
  578.  
  579.         String(const char * s = (const char *)0);
  580.         String(const String& s);
  581.         ~String() { delete str; }
  582.  
  583.         String& operator= (const char * s);
  584.         String& operator= (const String& s);
  585.         String& operator()(const char * s)
  586.             { *this = s; return *this; }
  587.  
  588.         int operator==(const String& cs) const
  589.             { return !cmp(cs); }
  590.         int operator> (const String& cs) const
  591.             { return (cmp(cs) > 0); }
  592.  
  593.         char& operator[](unsigned n)
  594.             { return ((n < len)?
  595.             str[n] : safety); }
  596.         operator const char *()  { return str; }
  597.  
  598.     };
  599.  
  600.     char String::safety = '\0';
  601.     size_t String::cmpn = 0;
  602.     int String::cmpi = 0;
  603.  
  604.     int String::cmp(const String& cs) const
  605.     {
  606.       if (str)
  607.         if (cs.str)
  608.           if (cmpn)
  609.             if (cmpi)
  610.           return strnicmp(str,cs.str,cmpn);
  611.             else
  612.           return strncmp(str,cs.str,cmpn);
  613.           else
  614.             if (cmpi)
  615.           return stricmp(str,cs.str);
  616.             else
  617.           return strcmp(str,cs.str);
  618.         else
  619.           return 1;
  620.       else
  621.         if (cs.str)
  622.           return -1;
  623.         else
  624.           return 0;
  625.     }
  626.  
  627.     String::String(const char * s)
  628.     {
  629.       str = (s? len = strlen(s), strdup(s)
  630.         : (char *)(len = 0));
  631.     }
  632.  
  633.     String::String(const String& s)
  634.     {
  635.       str = (((len = s.len) != 0)?
  636.         strdup(s.str) : (char *)0);
  637.     }
  638.  
  639.     String& String::operator=(const char * s)
  640.     {
  641.       delete str;
  642.       str = (s? len = strlen(s), strdup(s)
  643.         : (char *)(len = 0));
  644.       return *this;
  645.     }
  646.  
  647.     String& String::operator=(const String& s)
  648.     {
  649.       delete str;
  650.       str = (((len = s.len) != 0)?
  651.         strdup(s.str) : (char *)0);
  652.       return *this;
  653.     }
  654.  
  655.     ostream& operator<<(ostream& os, String& s)
  656.     {
  657.       os << s.len << '\n';
  658.       if (s.len)
  659.         os.write(s.str,s.len);
  660.       return os;
  661.     }
  662.  
  663.     istream& operator>>(istream& is, String& s)
  664.     {
  665.       delete s.str;
  666.       is >> s.len;
  667.       is.get();
  668.       if (!s.len)
  669.         s.str = (char *)0;
  670.       else if ((s.str = new char[s.len+1])
  671.         != (char *)0)  {
  672.         is.read(s.str,s.len);
  673.         s.str[s.len] = '\0';
  674.       }
  675.       else  {
  676.         is.ignore(s.len);
  677.         s.len = 0;
  678.       }
  679.       return is;
  680.     }
  681.  
  682. // See if you can pick out how each one of the six requisites 
  683. // for Container Lite's (form) template is satisfied.  We 
  684. // aren't finished with the example listing yet.
  685.  
  686.     //#define CPP_TEMPLATES
  687.  
  688.     #ifdef CPP_TEMPLATES
  689.         #include  "cl.hpt"
  690.         #define    CLString CL<String>
  691.     #else
  692.         #define    ITEM     String
  693.         #define    CL       CLString
  694.         #include  "cl.hpf"
  695.     #endif
  696.  
  697.     #define StrFile "ascii.tmp"
  698.  
  699.     main()
  700.     {
  701.         CLString sb(CL_DANDS);
  702.         String s("can be");
  703.         sb.insNew(&s);
  704.         sb.ins(new String("tamed!"));
  705.         sb.insNew(&s("Now strings"));
  706.         sb.sort();
  707.         sb.setCurNode(0);
  708.         sb.save(StrFile);
  709.         sb.allClr();
  710.         sb.load(StrFile);
  711.         while (sb.delAsg(&s))
  712.             cout << (const char *)s    << endl;
  713.         return 0;
  714.     }
  715.  
  716. If the String class were lacking a copy initializer constructor we 
  717. would simply include the ITEM_NO_COPYINIT macro, e.g.
  718.  
  719.     #ifdef CPP_TEMPLATES
  720.         #include  "cl.hpt"
  721.         #define    ITEM_NO_COPYINIT(String)
  722.         #define    CLString CL<String>
  723.     #else
  724.         #define    ITEM     String
  725.         #define    ITEM_NO_COPYINIT
  726.         #define    CL       CLString
  727.         #include  "cl.hpf"
  728.     #endif
  729.  
  730. since the default copy initializer supplied by the compiler would be 
  731. inappropriate for this String class.  Why?  Because the cloned 
  732. instance would point to the original string (String::str) and not a 
  733. duplicate like we intended.
  734.  
  735. Add whatever macros (as described before) you must to accurately 
  736. describe the deficiencies of your data type to Container Lite's (form) 
  737. template facility.
  738.  
  739.  
  740. 4.0  Protected Scope Virtual Functions
  741.  
  742. Perhaps your data type has deficiencies as far as Container Lite is 
  743. concerned, yet you still have a need for a full function container.  
  744. No problem, simply derive a new class from "cl" and override the 
  745. virtual functions yourself.  Container Lite (form) templates take the 
  746. same approach but there is a limit to how much they can be 
  747. reconfigured as we have seen in the previous sections.
  748.  
  749. Let's build a string resource for the internationalization of our 
  750. programs.  In our implementation, the first word in a string is the 
  751. token/key for the remainder of the string.  To change language 
  752. versions, edit the remainder of each string.  Since all token/key - 
  753. translation strings are to be located separately the task should be 
  754. easy.
  755.  
  756. We'll first set up a special compare function to search for and match 
  757. the token/key and then override DcmP(), Container Lite's protected 
  758. scope virtual function, to return this default whenever the current 
  759. compare function pointer is NULL, i.e. not supplied by the user which 
  760. it never can be since setCmP() is private.  The CLcmPcast() macro is 
  761. used below to typecast StrReSrc::cmp to the generic type voidCmP.  
  762. When building the string resource we'll be using static strings, 
  763. however when the resource is reloaded from disk they will become 
  764. dynamic.  Thus we only need to override Ddelete(), Dput(), and Dget() 
  765. while we can dispense with overriding Dassign() and Dnew() since we 
  766. won't be using any "Asg" or "New" primitives.
  767.  
  768.     // strresrc.cpp -- link with cl.obj
  769.  
  770.     #include <ctype.h>
  771.     #include <string.h>
  772.     #include "cl.hpp"
  773.  
  774.     class StrReSrc : cl  {
  775.       static int cmp(const char * S1, const char * S2);
  776.       static unsigned ridx;
  777.     protected:
  778.       virtual  voidCmP DcmP(voidCmP cmP)
  779.         { return (cmP? cmP :
  780.         CLcmPcast(cmp,void)); }
  781.       virtual  void   Ddelete(void * D)
  782.             { delete (char *) D; }
  783.       virtual  int    Dput(ostream& os, void * D);
  784.       virtual  void * Dget(istream& is);
  785.  
  786.     public:
  787.       StrReSrc() : cl(CL_DSTORE) {}
  788.       StrReSrc(const char * filename)
  789.         : cl(defaultConstruct)
  790.         { (void) cl::load(filename); }
  791.       int load(const char * filename)
  792.         { return cl::load(filename); }
  793.       int save(const char * filename)
  794.         { return cl::save(filename); }
  795.       int add(char * S)
  796.         { return (cl::insUnique(S)?1:0); }
  797.       const char * operator[](const char * S);
  798.       cl::allClr;
  799.       ~StrReSrc() { cl::destruct(); }
  800.     };
  801.  
  802.     int StrReSrc::cmp(const char * S1, const char * S2)
  803.     {
  804.       for (ridx = 0; S1[ridx] == S2[ridx] ; ridx++)
  805.       {
  806.         if (S1[ridx] == '\0')
  807.             return 1;
  808.         if (isspace(S1[ridx]))
  809.             return 0;
  810.       }
  811.       if (S1[ridx] == '\0' || S2[ridx] == '\0')
  812.         return 0;
  813.       return 1;
  814.     }
  815.  
  816.     unsigned StrReSrc::ridx;
  817.  
  818.     int StrReSrc::Dput(ostream& os, void * D)
  819.     {
  820.       int len = strlen((char *)D);
  821.       if (!(os << len << endm))
  822.         return 0;
  823.       if (len)
  824.         if (!os.write((char *)D,len))
  825.             return 0;
  826.       return 1;
  827.     }
  828.  
  829.     void * StrReSrc::Dget(istream& is)
  830.     {
  831.       int len;
  832.       is >> len >> nextm;
  833.       char * s = (char *)0;
  834.       if (len) if ((s = new char[len+1])
  835.         != (char *)0)  {
  836.         is.read(s,len);
  837.         s[len] = '\0';
  838.       }
  839.       else
  840.         is.ignore(len);
  841.       return (void *)s;
  842.     }
  843.  
  844.     const char * StrReSrc::operator[](const char * S)
  845.     {
  846.       if (S) if (findFirst(S))
  847.         return &(((char *)atGet(CurNode()))[ridx]);
  848.       return "not found";
  849.     }
  850.  
  851.     #define StrReSrcFile  "spanish.txt"
  852.  
  853.     main()
  854.     {
  855.         StrReSrc sr;
  856.         sr.add("one uno");
  857.         sr.add("two dos");
  858.         sr.add("three tres");
  859.         sr.add("three wrong");
  860.         sr.save(StrReSrcFile);
  861.         sr.allClr();
  862.         sr.load(StrReSrcFile);
  863.         cout << "Count to three in Spanish: "
  864.             << endl;
  865.         cout << sr["one"] << endl;
  866.         cout << sr["two"] << endl;
  867.         cout << sr["three"] << endl;
  868.         return 0;
  869.     }
  870.  
  871. The overloaded subscript operator[] calls findFirst() which uses the 
  872. special default compare function to match only the token/keys of the 
  873. strings.
  874.  
  875.  
  876. 5.0  Persistence
  877.  
  878. Examp311.cpp and StrReStr.cpp both showed their containers saved on 
  879. stream and later reloaded.  There is also a constructor that 
  880. reconstructs from a previously saved container.  Also the overloaded 
  881. stream insertion/extraction operators for the container allow you to 
  882. stream to/from open stream handles directly.
  883.  
  884.  
  885. 6.0  Polymorphic Nodes
  886.  
  887. Thus far we have only considered binding homogeneous data.  While 
  888. there is no reason why we can't bind heterogeneous data, it remains 
  889. difficult to provide the full spectrum of Container Lite functionality 
  890. owing to the complexities of having to provide a common assignment 
  891. operator, copy initializer constructor, stream operators, etcetera.  
  892. Of course if this functionality isn't required the use of CL_DEL_ONLY 
  893. with either a template or "form template" suffices nicely.
  894.  
  895. In order for you to more readily construct polymorphic clusters of 
  896. heterogeneous data the pitem.hpp/cpp files, supplied in the registered 
  897. version, declares/defines two classes, Streamable and Mutual, either 
  898. of which you can use as the polymorphic root for your family cluster 
  899. of classes.  Streamable forms the foundation of any persistent cluster 
  900. with run time typing, compatible assignment, cloning, and stream 
  901. processing of the various cluster members.  Mutual is itself derived 
  902. from Streamable and additionally provides for multiple reference 
  903. arbitration in RAM as well as automatic reference resolution during 
  904. streaming operations.  For example if an object with multiple 
  905. references is saved on a stream only one copy is saved.  Upon 
  906. reloading the multiple links are automatically reconstructed.
  907.  
  908. You can use either Streamable or Mutual in conjunction with Container 
  909. Lite to form polymorphic tree and graph structures.  A pitem.cbk file 
  910. is provided allowing you to cookbook your cluster derived classes.  It 
  911. is fully commented with step by step instructions.
  912.  
  913.  
  914. 7.0  Shareware Registration and Distribution
  915.  
  916. This version of Container Lite is licensed to you as shareware.  You 
  917. may try out Container Lite for a reasonable length of time (60 days) 
  918. to determine its fitness for your purposes.  If you decide to go on 
  919. using Container Lite you must obtain a user's license by registering!  
  920. You may also redistribute (share) the Container Lite shareware package 
  921. as outlined below.  Please contact PSW for OEM licensing information.
  922.  
  923.  
  924. 7.1  Registration
  925.  
  926. Registered users receive a full length (detailed reference) hard copy 
  927. manual, polymorphic node files (pitem.hpp, pitem.cpp, pitem.cbk), and 
  928. Container Lite source code broken down into its individual member 
  929. functions and grouped together in appropriate files to facilitate the 
  930. building of libraries (a makefile is also included).  Registered users 
  931. are granted a users license that allows for the use of Container Lite 
  932. in the development of software without royalty.
  933.  
  934. -----------------------------------------------------------
  935.  
  936. Container Lite v 1.81  Registration Form
  937.  
  938.  
  939. Please specify        3.5" ____   or    5.25" ____
  940. DOS formatted diskette
  941.  
  942.  
  943. Name _____________________________________
  944.  
  945. Company __________________________________
  946.  
  947. Address __________________________________
  948.  
  949. City _______________________
  950.  
  951. State/Province _____________
  952.  
  953. Zip/Postal Code ____________
  954.  
  955. Country ____________________
  956.  
  957.  
  958. Container Lite regular price:        $ 30
  959. Summer introductory pricing if
  960. ordered before Sept 21, 1993:        $ 20    ________
  961.  
  962.  
  963. Shipping/Handling for U.S.        $  4
  964. Foreign orders                $ 15    ________
  965.  
  966. Enclose U.S. bank draft (check)
  967. or money order payable to
  968. PSW / Power SoftWare  for:        total    ________
  969.  
  970. And mail to:
  971.  
  972. PSW / Power SoftWare
  973. P.O. Box 10072
  974. McLean, Virginia 22102 8072
  975. U.S.A.
  976. 703 759-3838
  977.  
  978. Sorry, we are not set up to accept credit card orders. 
  979.  
  980. -----------------------------------------------------------
  981.  
  982.  
  983. 7.2  Distribution
  984.  
  985. The Container Lite shareware package consists of the following files:
  986.  
  987.     cl.doc        cl.hpp        cl.cpp
  988.     cl.hpt        cl.hpf
  989.  
  990. Electronic Bulletin Board systems, including online commercial 
  991. services, may distribute the Container Lite shareware package provided 
  992. that the package remains intact and unaltered.
  993.  
  994. Commerical shareware distributors and computer user groups may also 
  995. distribute the Container Lite shareware package provided that the 
  996. package remains intact and unaltered in its own compressed file, 
  997. subdirectory, or diskette.  Any associated literature (sales, 
  998. promotional, or distribution) must also clearly explain the shareware 
  999. concept in a conspicuous manner.
  1000.  
  1001.  
  1002. 9.0  Miscellaneous
  1003.  
  1004. I hope you like the Container Lite.  If you have any questions, 
  1005. comments, or suggestions, please don't hesitate to call me.  I always 
  1006. look forward to hearing from you.
  1007.  
  1008. Happy programming!
  1009. John Small
  1010. (voice) 703 759-3838
  1011.