home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Supreme Volume 6 #1
/
swsii.zip
/
swsii
/
099
/
CL181.ZIP
/
CL.DOC
< prev
next >
Wrap
Text File
|
1993-04-26
|
34KB
|
1,011 lines
Container Lite (CL version 1.81: 4/26/93)
A Portable Persistent Hybrid Container Class for C++
Copyright 1993, John Webster Small, All rights reserved
PSW / Power SoftWare, P.O. Box 10072, McLean, VA 22102
(703) 759-3838
This version of Container Lite is licensed to you as shareware. For
details, see section 7 below.
1.0 Introduction
Applications using Container Lite typically use 80% less container
code than those coded with other commercial and compiler bundled
container class libraries. Use Container Lite repeatedly throughout
all your applications for even greater savings in design and coding
effort. Container Lite is ideal for coding games, rolling your own
application frameworks, and any application requiring user
configuration persistence.
Container Lite is a portable, persistent, hybrid container class
library compatible with compilers supporting AT&T's C++ 2.x, 3.x
standard. Container Lite is based upon a single container class
having a hybrid stack, queue, deque, list, array interface and
built-in sort, search, and iterate member functions.
1.1 Implementation
Container Lite is implemented as a dynamically sized array of void
pointers, pointing to the data you are collecting. Template
invocations additionally define primitives for on the fly copy
initialization, assignment, and destruction of your data nodes
automatically. Container Lite's "form template" provides the same
automatic code generation and strong type checking for compilers not
yet supporting templates.
1.2 Installation
Copy the following Container Lite files to the appropriate development
directory for your C++ compiler if you have not already done so.
cl.doc this file
cl.hpp Container Lite header
cl.cpp Container Lite source
cl.hpt Container Lite template
cl.hpf Container Lite form template
2.0 Hybrid Container
Always think of Container Lite in terms of its multi-faceted hybrid
behavior. One moment your container is a stack, the next a priority
queue or array, etcetera. With Container Lite you are never locked
into some arcane branch class of a convoluted, towering hierarchy like
you would be with a conventional "textbook" container implementation.
2.1 Dynamic Array
Container Lite's dynamic array behavior provides the foundation for
the rest of its inherent behaviors. Imagine an array in which you can
insert or extract cells at any location. Container Lite lets you
specify the granularity and hysteresis of this elasticity in
constructor and/or housekeeping calls with a delta parameter. In
other words, when a new cell needs to be added how many additional
spare cells should be provided to optimize future expansion efforts?
And how many spare cells should be allowed to accumulate before
compaction? The following list of member functions catalogs this
dynamic array behavior.
vector() Limit() setLimit()
pack() Delta() setDelta()
Nodes() MaxNodes() setMaxNodes()
vacancy() vacancyNonElastic()
atIns() atInsNew() atRmv()
allRmv() atDel() atDelAsg()
allDel() atPut() atPutNew()
atPutAsg() atGet() operator[]()
atGetAsg() atXchg() index()
forEach()
If you use the Container Lite template to generate a strongly typed
check wrapper class for your data, you can then use the above
primitives with "Asg", "New", and "Del" suffixes. These primitives
allow you to assign, clone, and delete your data nodes on the fly
which we will see in our first example.
// examp211.cpp -- link with cl.obj
#include <stdlib.h> // rand(), srand()
#include <time.h> // time_t, time()
#include "cl.hpt"
main()
{
time_t t;
srand((unsigned) time(&t));
CL<int> c(CL_DANDS,15);
int i = 0;
while (c.atInsNew(rand()%(c.Nodes()+1),&i))
i++;
cout << "0-14 in random order: " << endl;
while (c.atDelAsg(0,&i))
cout << i << endl;
return 0;
}
CL<int> generates a wrapper class for integers. The CL_DANDS flag
enables the container's "Asg", "New", "Del" and stream (S)torage
primitives. The numbers zero through fourteen are inserted at random
locations in the "array." AtInsNew() performs the same operation as
atIns() except the data is cloned on the fly and the clone is bound in
the container instead of the variable i. AtDelAsg() basically
performs an atGetAsg(), (As)si(g)ning (copying) the outgoing node to
the variable i, before performing an equivalent atDel() which is the
same thing as an atRmv() except the outgoing node is (Del)eted and not
just the cell (R)e(m)o(v)ed from the array.
If your compiler doesn't support templates you can use the "form
template" approach shown below instead.
// examp212.cpp -- link with cl.obj
#include <stdlib.h> // rand(), srand()
#include <time.h> // time_t, time()
#define ITEM int
#define CL CLint
#include "cl.hpf"
main()
{
time_t t;
srand((unsigned) time(&t));
CLint c(CL_DANDS,15);
int i = 0;
while (c.atInsNew(rand()%(c.Nodes()+1),&i))
i++;
cout << "0-14 in random order: " << endl;
while (c.atDelAsg(0,&i))
cout << i << endl;
return 0;
}
Notice that you must define ITEM as the parameter type that would
normally be passed as the template parameter, i.e. "#define ITEM int"
replaces "CL<int>". Likewise CL is defined as new type name so that
you use "CLint" in place of "CL<int>".
The manual that comes with the registered version of Container Lite
includes, among other things, a reference section detailing all
primitive operations. For now you will need to refer to cl.hpp
declarations and infer the operation of each member function from what
you glean from these examples. Also if you want to run these examples
yourself, you must use your ascii editor to extract them to their own
files. I didn't include them separately in order to reduce the
download size of this shareware version.
2.2 Stack-Queue-Deque
A stack provides LIFO (Last In, First Out) storage. Container Lite
has a complete set of stack primitives. These functions don't disturb
the list behavior of a container, e.g. current node setting. A queue
provides FIFO (First In, First Out) storage. And the deque
(pronounced deck) provides FOLO (First Out, Last Out) storage. Think
of the deque as a combination stack-queue with the additional
capability of being able to be popped from the rear. The following is
a list of the member functions supporting this stack-queue-deque
behavior:
push() pushNew() pop()
popDel() popDelAsg() top()
topAsg() insQ() insQNew()
unQ() unQDel() unQDelAsg()
rear() rearAsg() operator<<()
operator>>()
The example for this section uses a forms generated container of
floats.
// examp221.cpp -- link with cl.obj
#define ITEM float
#define CL CLfloat
#include "cl.hpf"
main()
{
CLfloat f(CL_DASSIGN|CL_DNEW|CL_DDELETE,10);
for (float i = 1.0; f.pushNew(&i); i++);
cout << "Count to 10: " << endl;
while (f.unQDelAsg(&i))
cout << i << endl;
return 0;
}
We can see individual flags being set in the constructor call this
time. Without CL_DNEW being set, pushNew() would have been inhibited
and all other member functions with the "New" suffix. Without
CL_DASSIGN and CL_DDELETE being set, unQDelAsg() would also have been
inhibited. This container is limited to a maximum of 10 items. If
you look in cl.hpf on line 238 you will see that the constructor
parameter named "maxNodes" is passed the actual parameter of 10. This
can be changed at any time with a call to setMaxNodes() that you saw
cataloged in the previous section. The unQ() family of member
functions work on the rear of the queue thus providing deque behavior.
The container's destructor is automatically called just before "return
0;". Since the previous while loop has already deleted all nodes
there is nothing left to delete. However, you should take note that
if a container's CL_DDELETE flag is raised the destructor will attempt
to delete any remaining nodes, otherwise the nodes are simply removed.
Thus you should typically segregate you containers into two groups:
those with static nodes and those with dynamic. You don't want to
delete something that isn't dynamically allocated! You can mix the
two types but it's safest not to set the CL_DDELETE flag since a
compiler generated destructor call could then jump out and bite you!
2.3 List
Any container can be treated as if it were a doubly linked list
thereby providing sequential storage. In a container's header
structure a current node index is maintained that lets the list
primitives know where the insertion or removal is to take place. The
dynamic array and stack-queue-list primitives don't disturb this
current node index unless of course it points to the cell that's being
removed. In that case the current node becomes undefined just like it
was when the container was first constructed. Here's a list of the
list primitives.
CurNode() setCurNode() ins()
insNew() rmv() del()
delAsg() put() putNew()
putAsg() get() getAsg()
next() operator++() nextAsg()
prev() operator--() prevAsg()
The example for this section contrasts the template and "form
template" approaches.
// examp231.cpp -- link with cl.obj
// #define CPP_TEMPLATES
#if defined(CPP_TEMPLATES)
#include "cl.hpt"
ITEM_DEL_ONLY(char)
#define CLstr CL<char>
#else
#define ITEM char
#define ITEM_DEL_ONLY
#define CL CLstr
#include "cl.hpf"
#endif
main()
{
CLstr s;
s.ins("C++ strings are ");
s.ins("not as well ");
s.ins("behaved as ");
// s.CurNode() == 2
s.insNew("This string won't appear!");
s << "they should be!";
s.setCurNode();
// s.CurNode == CL_NOTFOUND != 0
while (++s)
cout << (char *)s << endl;
return 0;
}
If you compiler supports templates you have the option of uncommenting
the second line "#define CPP_TEMPLATES." The ITEM_DEL_ONLY macro
turns off the "Asg", "New", and stream storage primitives. This is
necessary since the template instantiation can't correctly infer a
meaningful default copy initializer or assignment operator for C++
strings. Though stream insertion is possible it is precluded since a
default constructor for strings is required for stream extraction. We
are left with only being able to generate a string deletion method.
However, the string container is constructed in main() via a default
constructor. If you look in cl.hpp, cl.hpt, or cl.hpf, you will
notice that the default constructor sets the container's flags to
CL_BIND_ONLY. Thus "Del" primitives are inhibited at this point even
though the (form) template has provided for string deletion. This is
okay since all the strings bound in the container are statically
allocated and if we accidently call a "Del" primitive it will return
fail instead of crashing the program!
The nodes of a container are indexed just like an array, zero to the
number of nodes minus one. Hence the third node has an index of two.
Calling setCurNode() with the default parameter resets the list's
current to undefined, i.e. CL_NOTFOUND. When the list iterator is
subsequently applied, since no node is current to begin with, the
current node is set to zero which happens to be the first node in the
list. The list iterator returns a pointer to the current node. Upon
reaching the end of the list the current node is reset and the list
iterator returns the NULL pointer forcing the while loop to terminate.
(If the iterator were called again at this point, the current node
would wrap around to the front and the process would begin anew.)
Since containers are persistent, ambiguity exists when attempting to
insert a container into a stream. Did you want to insert the current
node or the whole container. In our example the explicit type cast
(char *) resolves this ambiguity and calls the implicit type cast
operator returning a pointer to the current node which happens to be a
string.
Other than type checking for strings, the template instantiations this
time gain us nothing in the way of automatic code generation. We
could have just as easily used a typeless container which the next
example demonstrates.
// examp232.cpp -- link with cl.obj
#include "cl.hpp"
main()
{
cl s;
s.ins("C++ strings are ");
s.ins("not as well ");
s.ins("behaved as ");
// s.CurNode() == 2
s.insNew("This string won't appear!");
s << "they should be!";
s.setCurNode();
// s.CurNode == CL_NOTFOUND != 0
while (++s)
cout << (char *)s.get() << endl;
return 0;
}
Notice that "cl" is a typeless container where as CL was used with
templates. Since the type is unknown an implicit type cast operator
is unavailable (the template generates this operator). Many times
when working with non persistent heterogeneous data it is easiest to
play fast and loose with a typeless container.
2.4 Sort-Search-Unique
Containers can also be sorted and searched on a default or user
supplied compare function. You don't have to derive a new class for
each different sort order! Only template and "form template"
generated containers have default compare functions (only if the
template parameter type has implicitly or explicitly defined
relational operators). Please note that these primitives affect the
list's current node setting.
Sorted() UnSort() setCmP()
CmP() sort() insSort()
insSortNew() insUnique() insUniqueNew()
findFirst() findNext() findLast()
findPrev() findAll()
With these primitives you can build bags, sets, dictionaries, etc.. In
our next example we will rely on the (form) template to generate
default copy initialization, assignment, and destruction code since
these are not declared or defined for the employee structure.
// examp241.cpp -- link with cl.obj
// #define CPP_TEMPLATES
#include <string.h>
struct employee {
char name[20];
unsigned salary;
employee *operator()
(const char *name, unsigned salary)
{
strncpy(this->name,name,20);
this->salary = salary;
return this;
}
employee(const char * name, unsigned salary)
{ (void) (*this)(name,salary); }
static int nameCmp(const employee * E1,
const employee * E2)
{ return strcmp(E1->name,E2->name); }
static int salaryCmp(const employee * E1,
const employee * E2)
{ return (int)(((long)(E1->salary))
-(long)(E2->salary)); }
};
#if defined(CPP_TEMPLATES)
#include "cl.hpt"
ITEM_NO_REL_STRM_OPS(employee)
#define CLe CL<employee>
#else
#define ITEM employee
#define ITEM_NO_REL_STRM_OPS
#define CL CLe
#include "cl.hpf"
#endif
main()
{
CLe ce(CL_DNEW|CL_DDELETE);
ce.setCmP(employee::salaryCmp);
ce.insSort(new employee("John Doe",1000));
employee e("John Small",100);
ce.insSort(new employee(e));
ce.insSortNew(e("Sally Good",10000));
cout << "Employees in increasing salary order: "
<< endl;
for (ce.setCurNode(0); ce; ++ce)
cout << "\nEmployee name: "
<< ((employee *)ce)->name << endl
<< " salary: "
<< ce.get()->salary << endl;
ce.setCmP(employee::nameCmp);
cout << "\n\nFind employee: "
<< e.name << endl;
if (ce.findFirst(&e))
cout << "\nEmployee name: "
<< ((employee *)ce)->name << endl
<< " salary: "
<< ce.get()->salary << endl;
else
cout << "\nnot found" << endl;
return 0;
}
The ITEM_NO_REL_STRM_OPS macro is used to turn off (form) template
generation of compare and stream operations since the employee
structure doesn't overload relational or stream operators. Thus even
if the constructor call raised the CL_DSTORE flag the only thing that
would be stored on stream for the container would be its header
information but not the nodes.
This example performs an insertion sort on increasing salary order of
three dynamically allocated employee records using various techniques
to test your C++ acumen. The sort order was determined by the
previous call to setCmP(). Notice the for loop initializes the
current node to zero, the first node in the list. The for loop's test
expression calls the implicit type cast operator, generated by the
(form) template, which returns a pointer to the current node. The for
loop's reinitialization statement calls the container's list iterator.
Now setCmP() is called again to key in on names. If sort() were
called next, the list would be sorted in alphabetical order. Instead
findFirst() is used to find the matching name. The container is able
to determine if it is still in sorted order since the last sort. If
it is, findFirst() will use a binary search algorithm otherwise a
linear search will be performed. FindNext() always uses a linear
search; however, if the list is in sorted order only the next node is
tested for a match and the result returned. If the list is not sorted
then the search for a match will continue throughout the remainder of
the list. Once a container is sorted, it will remain in a sorted
state, as far as the container is concerned, until the compare
function is changed or a new node is added without insSort???() or
insUnique???(). However, it is possible for you to access a node,
modifying the key value, without the container being aware that the
sorted order has been spoiled. Be sure to use UnSort() to let the
container know if it is no longer sorted in these cases.
3.0 Strong Type Checking
Container Lite's foundation class is named "cl". This class used by
itself performs no type checking on the data being bound. These
typeless containers are useful for quick and dirty handling of
heterogeneous data types. However, by using a template a container
can be made to impose strong type checking for any type of data at
compile time, thereby restricting the use of the container to a
particular data type. Even if your compiler doesn't support
templates, strong type checking can still be achieved with Container
Lite's "form templates." You have already seen all three approaches
in the examples of the previous section.
3.1 A Well Endowed Data Type
A fully functional container requires its nodes' data type to be well
endowed or more precisely that the nodes' data type (ITEM) must have
(either implicitly or explicitly defined):
1. an overloaded equality operator, i.e.
int ITEM::operator==(const ITEM&) const;
or
int operator==(const ITEM&, const ITEM&);
//friend
and an overloaded greater than operator, i.e.
int ITEM::operator>(const ITEM&) const;
or
int operator>(const ITEM&, const ITEM&);
2. a copy initializer constructor, i.e.
ITEM::ITEM(const ITEM&);
3, an overloaded assignment operator, i.e.
ITEM& ITEM::operator=(const ITEM&);
4. an overloaded stream insertion operator, i.e.
ostream& operator<<(ostream&,ITEM&);
5. an overloaded stream extraction operator, i.e.
istream& operator>>(istream&,ITEM&);
and a default constructor, i.e.
ITEM::ITEM();
and
6. a destructor if one is required, i.e.
ITEM::~ITEM();
Overloaded relational operators, and stream operators can't be
inferred implicitly by the compiler for non native types. In
examp241, the employee structure lacked both the requisite relational
and stream operators. Hence the ITEM_NO_REL_STRM_OPS macro was
employed in conjunction with the (form) template. The macros
ITEM_NO_REL_OPS, ITEM_NO_STRM_INSERT, and ITEM_NO_STRM_EXTRACT could
have been used individually instead for more precise (form) template
tuning.
The compiler can supply a default copy initializer constructor and
assignment operator which is what examp241 relied on. Sometimes these
defaults are unacceptable as they were in examp231 and only default
deletion was okay. Thus examp231 used the ITEM_DEL_ONLY macro. The
only way to avoid default deletion is to leave a container's
CL_DDELETE flag reset. If you look cl.hpt you will see that
ITEM_DEL_ONLY is defined as:
#define ITEM_DEL_ONLY(ITEM) \
ITEM_NO_REL_OPS(ITEM) \
ITEM_NO_ASSIGN(ITEM) \
ITEM_NO_COPYINIT(ITEM) \
ITEM_NO_STRM_INSERT(ITEM) \
ITEM_NO_STRM_EXTRACT(ITEM)
Of course you can fine tune a (form) template instantiation by
invoking only those macros that accurately define the deficiencies of
you data type.
Let's now look at a well endowed (as far as Container Lite's template
facility is concerned) string class that will yield us a fully
functional container of strings.
// examp311.cpp -- link with cl.obj
#include <string.h>
#include <iostream.h>
class String {
char *str;
size_t len;
friend ostream& operator<<
(ostream& os, String& s);
friend istream& operator>>
(istream& is, String& s);
int cmp(const String& cs) const;
public:
static char safety;
static size_t cmpn;
static int cmpi;
String(const char * s = (const char *)0);
String(const String& s);
~String() { delete str; }
String& operator= (const char * s);
String& operator= (const String& s);
String& operator()(const char * s)
{ *this = s; return *this; }
int operator==(const String& cs) const
{ return !cmp(cs); }
int operator> (const String& cs) const
{ return (cmp(cs) > 0); }
char& operator[](unsigned n)
{ return ((n < len)?
str[n] : safety); }
operator const char *() { return str; }
};
char String::safety = '\0';
size_t String::cmpn = 0;
int String::cmpi = 0;
int String::cmp(const String& cs) const
{
if (str)
if (cs.str)
if (cmpn)
if (cmpi)
return strnicmp(str,cs.str,cmpn);
else
return strncmp(str,cs.str,cmpn);
else
if (cmpi)
return stricmp(str,cs.str);
else
return strcmp(str,cs.str);
else
return 1;
else
if (cs.str)
return -1;
else
return 0;
}
String::String(const char * s)
{
str = (s? len = strlen(s), strdup(s)
: (char *)(len = 0));
}
String::String(const String& s)
{
str = (((len = s.len) != 0)?
strdup(s.str) : (char *)0);
}
String& String::operator=(const char * s)
{
delete str;
str = (s? len = strlen(s), strdup(s)
: (char *)(len = 0));
return *this;
}
String& String::operator=(const String& s)
{
delete str;
str = (((len = s.len) != 0)?
strdup(s.str) : (char *)0);
return *this;
}
ostream& operator<<(ostream& os, String& s)
{
os << s.len << '\n';
if (s.len)
os.write(s.str,s.len);
return os;
}
istream& operator>>(istream& is, String& s)
{
delete s.str;
is >> s.len;
is.get();
if (!s.len)
s.str = (char *)0;
else if ((s.str = new char[s.len+1])
!= (char *)0) {
is.read(s.str,s.len);
s.str[s.len] = '\0';
}
else {
is.ignore(s.len);
s.len = 0;
}
return is;
}
// See if you can pick out how each one of the six requisites
// for Container Lite's (form) template is satisfied. We
// aren't finished with the example listing yet.
//#define CPP_TEMPLATES
#ifdef CPP_TEMPLATES
#include "cl.hpt"
#define CLString CL<String>
#else
#define ITEM String
#define CL CLString
#include "cl.hpf"
#endif
#define StrFile "ascii.tmp"
main()
{
CLString sb(CL_DANDS);
String s("can be");
sb.insNew(&s);
sb.ins(new String("tamed!"));
sb.insNew(&s("Now strings"));
sb.sort();
sb.setCurNode(0);
sb.save(StrFile);
sb.allClr();
sb.load(StrFile);
while (sb.delAsg(&s))
cout << (const char *)s << endl;
return 0;
}
If the String class were lacking a copy initializer constructor we
would simply include the ITEM_NO_COPYINIT macro, e.g.
#ifdef CPP_TEMPLATES
#include "cl.hpt"
#define ITEM_NO_COPYINIT(String)
#define CLString CL<String>
#else
#define ITEM String
#define ITEM_NO_COPYINIT
#define CL CLString
#include "cl.hpf"
#endif
since the default copy initializer supplied by the compiler would be
inappropriate for this String class. Why? Because the cloned
instance would point to the original string (String::str) and not a
duplicate like we intended.
Add whatever macros (as described before) you must to accurately
describe the deficiencies of your data type to Container Lite's (form)
template facility.
4.0 Protected Scope Virtual Functions
Perhaps your data type has deficiencies as far as Container Lite is
concerned, yet you still have a need for a full function container.
No problem, simply derive a new class from "cl" and override the
virtual functions yourself. Container Lite (form) templates take the
same approach but there is a limit to how much they can be
reconfigured as we have seen in the previous sections.
Let's build a string resource for the internationalization of our
programs. In our implementation, the first word in a string is the
token/key for the remainder of the string. To change language
versions, edit the remainder of each string. Since all token/key -
translation strings are to be located separately the task should be
easy.
We'll first set up a special compare function to search for and match
the token/key and then override DcmP(), Container Lite's protected
scope virtual function, to return this default whenever the current
compare function pointer is NULL, i.e. not supplied by the user which
it never can be since setCmP() is private. The CLcmPcast() macro is
used below to typecast StrReSrc::cmp to the generic type voidCmP.
When building the string resource we'll be using static strings,
however when the resource is reloaded from disk they will become
dynamic. Thus we only need to override Ddelete(), Dput(), and Dget()
while we can dispense with overriding Dassign() and Dnew() since we
won't be using any "Asg" or "New" primitives.
// strresrc.cpp -- link with cl.obj
#include <ctype.h>
#include <string.h>
#include "cl.hpp"
class StrReSrc : cl {
static int cmp(const char * S1, const char * S2);
static unsigned ridx;
protected:
virtual voidCmP DcmP(voidCmP cmP)
{ return (cmP? cmP :
CLcmPcast(cmp,void)); }
virtual void Ddelete(void * D)
{ delete (char *) D; }
virtual int Dput(ostream& os, void * D);
virtual void * Dget(istream& is);
public:
StrReSrc() : cl(CL_DSTORE) {}
StrReSrc(const char * filename)
: cl(defaultConstruct)
{ (void) cl::load(filename); }
int load(const char * filename)
{ return cl::load(filename); }
int save(const char * filename)
{ return cl::save(filename); }
int add(char * S)
{ return (cl::insUnique(S)?1:0); }
const char * operator[](const char * S);
cl::allClr;
~StrReSrc() { cl::destruct(); }
};
int StrReSrc::cmp(const char * S1, const char * S2)
{
for (ridx = 0; S1[ridx] == S2[ridx] ; ridx++)
{
if (S1[ridx] == '\0')
return 1;
if (isspace(S1[ridx]))
return 0;
}
if (S1[ridx] == '\0' || S2[ridx] == '\0')
return 0;
return 1;
}
unsigned StrReSrc::ridx;
int StrReSrc::Dput(ostream& os, void * D)
{
int len = strlen((char *)D);
if (!(os << len << endm))
return 0;
if (len)
if (!os.write((char *)D,len))
return 0;
return 1;
}
void * StrReSrc::Dget(istream& is)
{
int len;
is >> len >> nextm;
char * s = (char *)0;
if (len) if ((s = new char[len+1])
!= (char *)0) {
is.read(s,len);
s[len] = '\0';
}
else
is.ignore(len);
return (void *)s;
}
const char * StrReSrc::operator[](const char * S)
{
if (S) if (findFirst(S))
return &(((char *)atGet(CurNode()))[ridx]);
return "not found";
}
#define StrReSrcFile "spanish.txt"
main()
{
StrReSrc sr;
sr.add("one uno");
sr.add("two dos");
sr.add("three tres");
sr.add("three wrong");
sr.save(StrReSrcFile);
sr.allClr();
sr.load(StrReSrcFile);
cout << "Count to three in Spanish: "
<< endl;
cout << sr["one"] << endl;
cout << sr["two"] << endl;
cout << sr["three"] << endl;
return 0;
}
The overloaded subscript operator[] calls findFirst() which uses the
special default compare function to match only the token/keys of the
strings.
5.0 Persistence
Examp311.cpp and StrReStr.cpp both showed their containers saved on
stream and later reloaded. There is also a constructor that
reconstructs from a previously saved container. Also the overloaded
stream insertion/extraction operators for the container allow you to
stream to/from open stream handles directly.
6.0 Polymorphic Nodes
Thus far we have only considered binding homogeneous data. While
there is no reason why we can't bind heterogeneous data, it remains
difficult to provide the full spectrum of Container Lite functionality
owing to the complexities of having to provide a common assignment
operator, copy initializer constructor, stream operators, etcetera.
Of course if this functionality isn't required the use of CL_DEL_ONLY
with either a template or "form template" suffices nicely.
In order for you to more readily construct polymorphic clusters of
heterogeneous data the pitem.hpp/cpp files, supplied in the registered
version, declares/defines two classes, Streamable and Mutual, either
of which you can use as the polymorphic root for your family cluster
of classes. Streamable forms the foundation of any persistent cluster
with run time typing, compatible assignment, cloning, and stream
processing of the various cluster members. Mutual is itself derived
from Streamable and additionally provides for multiple reference
arbitration in RAM as well as automatic reference resolution during
streaming operations. For example if an object with multiple
references is saved on a stream only one copy is saved. Upon
reloading the multiple links are automatically reconstructed.
You can use either Streamable or Mutual in conjunction with Container
Lite to form polymorphic tree and graph structures. A pitem.cbk file
is provided allowing you to cookbook your cluster derived classes. It
is fully commented with step by step instructions.
7.0 Shareware Registration and Distribution
This version of Container Lite is licensed to you as shareware. You
may try out Container Lite for a reasonable length of time (60 days)
to determine its fitness for your purposes. If you decide to go on
using Container Lite you must obtain a user's license by registering!
You may also redistribute (share) the Container Lite shareware package
as outlined below. Please contact PSW for OEM licensing information.
7.1 Registration
Registered users receive a full length (detailed reference) hard copy
manual, polymorphic node files (pitem.hpp, pitem.cpp, pitem.cbk), and
Container Lite source code broken down into its individual member
functions and grouped together in appropriate files to facilitate the
building of libraries (a makefile is also included). Registered users
are granted a users license that allows for the use of Container Lite
in the development of software without royalty.
-----------------------------------------------------------
Container Lite v 1.81 Registration Form
Please specify 3.5" ____ or 5.25" ____
DOS formatted diskette
Name _____________________________________
Company __________________________________
Address __________________________________
City _______________________
State/Province _____________
Zip/Postal Code ____________
Country ____________________
Container Lite regular price: $ 30
Summer introductory pricing if
ordered before Sept 21, 1993: $ 20 ________
Shipping/Handling for U.S. $ 4
Foreign orders $ 15 ________
Enclose U.S. bank draft (check)
or money order payable to
PSW / Power SoftWare for: total ________
And mail to:
PSW / Power SoftWare
P.O. Box 10072
McLean, Virginia 22102 8072
U.S.A.
703 759-3838
Sorry, we are not set up to accept credit card orders.
-----------------------------------------------------------
7.2 Distribution
The Container Lite shareware package consists of the following files:
cl.doc cl.hpp cl.cpp
cl.hpt cl.hpf
Electronic Bulletin Board systems, including online commercial
services, may distribute the Container Lite shareware package provided
that the package remains intact and unaltered.
Commerical shareware distributors and computer user groups may also
distribute the Container Lite shareware package provided that the
package remains intact and unaltered in its own compressed file,
subdirectory, or diskette. Any associated literature (sales,
promotional, or distribution) must also clearly explain the shareware
concept in a conspicuous manner.
9.0 Miscellaneous
I hope you like the Container Lite. If you have any questions,
comments, or suggestions, please don't hesitate to call me. I always
look forward to hearing from you.
Happy programming!
John Small
(voice) 703 759-3838