home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
a_arrays.zip
/
ASSOC.HPP
< prev
next >
Wrap
Text File
|
1994-10-31
|
13KB
|
287 lines
/***************************************************************
* file: ASSOC.HPP
* purpose: template class for associative arrays
* contains:
* ASSOCIATION_BASE - core routines for the ASSOCIATION template
* ASSOCIATION - association between strings and data objects
* copyright: 1994 by David Weber. Unlimited use granted in EXE, OBJ,
* or LIB formats. Do not sell as source without asking first.
* environment:
* tested Borland C++ 4.01 and Zortech C++ 3.1r2
* history:
* 10-02-94 - initial code, based on an earlier C module
**************************************************************/
#ifndef _ASSOC
#define _ASSOC
// needed goodies
#include <string.h>
// Feature controls
#define ASSOC_CASE_SENSITIVE 1 // string case sensitivity (1 or 0)
#define ASSOC_EXCEPTIONS 0 // environment supports C++ exceptions
#define ASSOC_NEW_CASTS 0 // environment supports new C++ casts
// case sensitivity - This could be done dynamically with a function ptr in the
// class, but would disallow the compiler to do optimization via intrinsic
// function expansion.
#if ASSOC_CASE_SENSITIVE
#define ASSOC_STRCMP strcmp
#define ASSOC_MAP(c) (c)
#else
#include <ctype.h>
#define ASSOC_STRCMP stricmp
#define ASSOC_MAP(c) toupper(c)
#endif
// The only place exceptions occur are resource failure with the "new" calls.
// If the environment supports C++ exceptions and a failure occurs, a handler
// somewhere, even if it's terminate(), will take care of it. Without
// exception handling we just shut down the farm via assert() and abort()
#if ASSOC_EXCEPTIONS
#define ASSOC_MEM_CHECK(p)
#else
#include <stdlib.h>
#include <assert.h>
#define ASSOC_MEM_CHECK(p) { if ((p) == 0) { assert((p) != 0); abort(); } }
#endif
// old versus new casting, not much gained here except you can search for casts
#if ASSOC_NEW_CASTS
#define ASSOC_CAST(cast,item) reinterpret_cast<cast>(item)
#else
#define ASSOC_CAST(cast,item) (cast)(item)
#endif
// defines
const int DEFAULT_ASSOC_SIZE = 64; // default estimated size of array
const unsigned int ASSOC_NULL = ~0; // end of chain
// The base class for associative arrays. You should NOT make an instance
// of this class. Instead use the ASSOCIATON template below
class ASSOCIATION_BASE
{
protected: // protect everything
ASSOCIATION_BASE(unsigned int data_size,unsigned int estimated_size);
~ASSOCIATION_BASE(); // destructor - make virtual if extending
// inheritance and/or pointing along the chain
ASSOCIATION_BASE(const ASSOCIATION_BASE &); // copy constructor
ASSOCIATION_BASE &operator=(const ASSOCIATION_BASE &); // assignment
ASSOCIATION_BASE(const char **keys,const char *data, // static initializer
unsigned int data_size,unsigned int count);
unsigned int size(void) // how many data elements in array
{ return fill_level; }
int insert(const char *key,const char *data);// add an element
int remove(const char *key); // remove an element
char *find(const char *key); // find an element
const char *first(void); // traversal functions
const char *next(void);
int operator!() // test array for problems
{ return hash_list==0 || key_list==0 || link_list==0 || data_list==0; }
int operator()(const char *key) // existence operator
{ return find(key) != 0; }
char &operator[](const char *key)// access operator
{ return *reference(key); }
char *reference(const char *key);// get a reference to data or insert if needed
unsigned int hash(const char *key);// hash function
void allocate_array(void); // get enough space for the array
int expand_array(void); // resize array
void clear_pointers(void) // clear all pointers
{ hash_list = 0; key_list = 0; link_list = 0; data_list = 0; }
void delete_pointers(void) // delete all pointers
{ delete [] hash_list; delete [] key_list; delete [] link_list; delete [] data_list; }
unsigned int array_size; // full size of array
unsigned int fill_level; // data entries currently in array
unsigned int *hash_list; // hash indexed array of chains
const char **key_list; // storage for key pointers
unsigned int *link_list; // storage for key linkages
char *data_list; // storage for data expressed as char
unsigned int sizeofdata; // size of data objects in bytes
unsigned int iteration; // current iteration position in data
};
// ASSOCIATION - associative array template
// Use this class template for creating instances when you will be
// storing associations between character strings and data objects.
// There are three ways to use this template:
// direct storage - data are stored directly in the associative array.
// good for small classes or native types
// ASSOCIATION<myclass> direct_array(estimated_size);
// value = *direct_array.find("key");
// indirect storage - pointers to data are stored in the array.
// good for large classes or pointers to things that vary in size
// ASSOCIATION<myclass *> indirect_array(estimated_size);
// ptr_to_value = *indirect_array.find("key");
// value = **indirect_array.find("key");
// function pointer storage - pointers to functions are stored.
// ASSOCIATION<int (*)(int)> func_ptr_array(estimated_size);
// int (**fptr)(int);
// if ((fptr = func_ptr_array.find("key")) != 0) // always test
// function_return = (**fptr)(parameter);
// You are responsible for the string storage. In the case of indirect
// storage you are also responsible for storing the data.
// example:
// ASSOCIATION<myclass> assoc_array(estimated_size); // declaration
// if (!assoc_array) { class is unusable; } // validity
// assoc_array.insert("xray",myclass_instance1); // insert, returns 0 on failure
// assoc_array.insert("yodel",myclass_instance2);
// assoc_array.insert("zorro",myclass_instance3);
// assoc_array.remove("yodel"); // delete, returns 0 if not there
// cout << "Size is " << assoc_array.size() << "\n"; // size is 2
// cout << "zorro is " << *assoc_array.find("zorro") << "\n";// find
// assert(assoc_array.find("garbage") == 0); // failed find returns 0
// if ((const char *p = assoc_array.first()) != 0) // iterate over set
// { // do not insert or remove while iterating
// do
// {
// cout << p << "\t\t" << *assoc_array.find(p) << "\n";
// } while ((p = assoc_array.next()) != 0);
// }
// other uses:
// copy constructor
// ASSOCIATION<int> x; // fill x with stuff
// ASSOCIATION<int> y(x);
// assignment
// ASSOCIATION<int> x; // fill x with stuff
// ASSOCIATION<int> y;
// y = x;
// static initialization
// static const char *keys[] = { "key1","key2", "key3" };// note: const
// static int data[] = { 1,2,3 };
// ASSOCIATION<int> x(keys,data,sizeof(keys)/sizeof(keys[0]));
template <class T> class ASSOCIATION : public ASSOCIATION_BASE
{
public:
ASSOCIATION(unsigned int estimated_size = DEFAULT_ASSOC_SIZE) :
ASSOCIATION_BASE(sizeof(T),estimated_size) { } // default constructor
// destructor and copy constructor found in ASSOCIATION_BASE
ASSOCIATION<T> &operator=(const ASSOCIATION<T> &original)// assignment
{ ASSOCIATION_BASE::operator=(original); return *this; }
// static initializer
ASSOCIATION(const char **keys,T *data,unsigned int count) :
ASSOCIATION_BASE(keys,ASSOC_CAST(const char *,data),sizeof(T),count) { }
unsigned int size(void) // how many data elements in array
{ return fill_level; }
int insert(const char *key,T data) // add an element
{ return ASSOCIATION_BASE::insert(key,ASSOC_CAST(const char *,&data)); }
int remove(const char *key) // remove an element
{ return ASSOCIATION_BASE::remove(key); }
T *find(const char *key) // find an element
{ return ASSOC_CAST(T *,ASSOCIATION_BASE::find(key)); }
const char *first(void) // traversal functions
{ return ASSOCIATION_BASE::first(); }
const char *next(void)
{ return ASSOCIATION_BASE::next(); }
int operator!() // test array for problems
{ return ASSOCIATION_BASE::operator!(); }
int operator()(const char *key) // existence operator
{ return ASSOCIATION_BASE::find(key) != 0; }
T &operator[](const char *key) // access operator
{ return *(ASSOC_CAST(T *,ASSOCIATION_BASE::reference(key))); }
};
// ASSOC_STORED - ASSOCIATION class with storage for keys
// This class is almost identical to ASSOCIATION except that it maintains the
// storage for the keys. The interface is the same but the static initializer
// is left out. If it is static, the keys are already stored. So why bother?
template <class T> class ASSOC_STORED : public ASSOCIATION_BASE
{
public:
ASSOC_STORED(unsigned int estimated_size = DEFAULT_ASSOC_SIZE) :
ASSOCIATION_BASE(sizeof(T),estimated_size) { } // default constructor
~ASSOC_STORED(); // destructor
ASSOC_STORED(const ASSOC_STORED<T> &original) : ASSOCIATION_BASE(original)
{ dup_keys(); } // copy constructor
ASSOC_STORED<T> &operator=(const ASSOC_STORED<T> &original)
{ // assignment
ASSOCIATION_BASE::operator=(original);
dup_keys();
return *this;
}
unsigned int size(void) // how many data elements in array
{ return fill_level; }
int insert(const char *key,T data) // add an element
{
if (key == 0)
return 0;
char *p = new char[strlen(key)+1];
ASSOC_MEM_CHECK(p);
strcpy(p,key);
return ASSOCIATION_BASE::insert(p,ASSOC_CAST(const char *,&data));
}
int remove(const char *key); // remove an element
T *find(const char *key) // find an element
{ return ASSOC_CAST(T *,ASSOCIATION_BASE::find(key)); }
const char *first(void) // traversal functions
{ return ASSOCIATION_BASE::first(); }
const char *next(void)
{ return ASSOCIATION_BASE::next(); }
int operator!() // test array for problems
{ return ASSOCIATION_BASE::operator!(); }
int operator()(const char *key) // existence operator
{ return ASSOCIATION_BASE::find(key) != 0; }
T &operator[](const char *key) // access operator
{
char *p = ASSOCIATION_BASE::find(key);
if (p == 0)
{
if (key == 0)
return *(ASSOC_CAST(T *,0));// garbage in, garbage out
char *p = new char[strlen(key)+1];
ASSOC_MEM_CHECK(p);
strcpy(p,key);
return *(ASSOC_CAST(T *,ASSOCIATION_BASE::reference(p)));
}
return *(ASSOC_CAST(T *,p));
}
private:
void dup_keys(void);
};
// functions out of line cuz looped, large and/or called infrequently
// destructor
template <class T> ASSOC_STORED<T>::~ASSOC_STORED()
{
for (unsigned int i = 0 ; i < fill_level ; i++)
delete [] ASSOC_CAST(char *,key_list[i]);
}
// remove
template <class T> int ASSOC_STORED<T>::remove(const char *key)
{
char *data = ASSOCIATION_BASE::find(key);
if (data != 0)
{
data = ASSOC_CAST(char *,key_list[(data-data_list)/sizeofdata]);
if (ASSOCIATION_BASE::remove(key))
{
delete [] data;
return 1;
}
}
return 0;
}
// duplicate the full set of keys
template <class T> void ASSOC_STORED<T>::dup_keys(void)
{
char *newkey;
const char *oldkey;
for (unsigned int i = 0 ; i < fill_level ; i++)
{ // duplicate keys
oldkey = key_list[i];
newkey = new char[strlen(oldkey)+1];
ASSOC_MEM_CHECK(newkey);
strcpy(newkey,oldkey);
key_list[i] = newkey;
}
}
#endif // _ASSOC