Patently UNTRUE: "Macros are evil. Period"
I assume you never used a proper macro language such as m4. I assume you never used the -E option of gcc.
Here is an example of an m4-based vector (and a hashtable) class and I challenge you build something equivalent with templates. Something which gives sane error messages, which will allow for proper code navigation (where is the payload constructor called ??), which allows for proper debugging of the data structure. I bet you can't get it done.
So your statement holds some water if you refer to "C macros", but even then, some people manage to build huge systems (2000 modules and bigger) based on macro-constructed container classes. E.g. CATIA.
//vector class
//$1: type of payload
define(generate_vector,
class vector_$1
{
$1* _array;
unsigned long long _length;
unsigned long long _capacity;
public:
vector_$1()
{
_array = new $1[1];
_length = 0;
_capacity = 1;
}
void operator = (const vector_$1& other)
{
ensureCapacity(other._length);
_length = other._length;
for(unsigned long long i=0; i < _length; i++)
{
_array[i] = other._array[i];
}
}
vector_$1(const vector_$1& other)
{
_array = NULL;
_length = 0;
_capacity = 0;
*this = other;
}
~vector_$1()
{
_length = 0;
_capacity = 0;
delete[] _array;
_array = NULL;
}
unsigned long long length() const
{
return _length;
}
void add($1 element)
{
ensureCapacity(_length + 1);
_array[_length++] = element;
}
void ensureCapacity(unsigned long long count)
{
if(count >= _capacity)
{
unsigned long long newCap = _capacity * 2;
if(newCap < count)
{
newCap = count;
}
$1* newArray = new $1[newCap];
for(unsigned long long i=0; i < _length; i++)
{
newArray[i] = _array[i];
}
_capacity = newCap;
delete[] _array;
_array = newArray;
}
}
void getAt(unsigned long long index,$1& retVal) const
{
if( index >= _length)
{
//deterministically crash upon out-of-bounds.
//core file can be easily debugged
int* crashPtr = NULL; crashPtr[0] = 0;
}
retVal = _array[index];
}
void setAt(unsigned long long index,const $1& val)
{
if( index >= _length)
{
//deterministically crash upon out-of-bounds.
//core file can be easily debugged
int* crashPtr = NULL; crashPtr[0] = 0;
}
_array[index] = val;
}
$1 operator[](unsigned long long index)
{
if( index >= _length)
{
//deterministically crash upon out-of-bounds.
//core file can be easily debugged
int* crashPtr = NULL; crashPtr[0] = 0;
}
return _array[index];
}
const $1 operator[](unsigned long long index) const
{
if( index >= _length)
{
//deterministically crash upon out-of-bounds.
//core file can be easily debugged
int* crashPtr = NULL; crashPtr[0] = 0;
}
return _array[index];
}
};
)
//$1: type of key
//$2: type of value
//$3: type of HashHelper
define(generate_hashtable,
struct entry_$1_$2_$3
{
$1 _key;
$2 _value;
entry_$1_$2_$3* _nextInBin;
bool _isOccupied;
entry_$1_$2_$3()
{
_nextInBin = NULL;
_isOccupied = false;
}
};
class hashtable_$1_$2_$3
{
entry_$1_$2_$3* _bins;
unsigned long long _numBins;
unsigned long long _numElements;
static void insert(entry_$1_$2_$3* bins,
unsigned long long binCount,
const $1& key,
const $2& value)
{
unsigned long long index = $3::hash( key ) % binCount;
if( bins[index]._isOccupied )
{
entry_$1_$2_$3* ptr = &bins[index];
entry_$1_$2_$3* lastPtr;
do{
lastPtr = ptr;
if( $3::equals(ptr->_key,key) )
{
break;
}
ptr = ptr->_nextInBin;
}
while( ptr != NULL );
entry_$1_$2_$3* newEntry = new entry_$1_$2_$3;
newEntry->_isOccupied = true;
newEntry->_key = key;
newEntry->_value = value;
lastPtr->_nextInBin = newEntry;
}
else
{
bins[index]._isOccupied = true;
bins[index]._key = key;
bins[index]._value = value;
}
}
public:
hashtable_$1_$2_$3()
{
_bins = new entry_$1_$2_$3[1];
_numBins = 1;
_numElements = 0;
}
~hashtable_$1_$2_$3()
{
for(unsigned long long i=0; i < _numBins; i++)
{
if( _bins[i]._isOccupied )
{
entry_$1_$2_$3* ptr = &_bins[i];
do{
entry_$1_$2_$3* tobeDeleted = ptr;
ptr = ptr->_nextInBin;
if( tobeDeleted != &_bins[i] )
{
delete tobeDeleted;
}
}
while( ptr != NULL );
}
}
delete[] _bins;
_numBins = 0;
_numElements = 0;
}
void insert(const $1& key,const $2& value)
{
if( _numBins <= _numElements )
{
//grow table
unsigned long long newSize = _numBins * 2;
entry_$1_$2_$3* _newBins = new entry_$1_$2_$3[newSize];
for(unsigned long long i=0; i < _numBins; i++)
{
if( _bins[i]._isOccupied )
{
entry_$1_$2_$3* ptr = &_bins[i];
do
{
insert( _newBins, newSize, ptr->_key,ptr->_value );
entry_$1_$2_$3* tobeDeleted = ptr;
ptr = ptr->_nextInBin;
if( tobeDeleted != &_bins[i] )
{
delete tobeDeleted;
}
}
while( ptr != NULL );
}
}
delete[] _bins;
_bins = _newBins;
_numBins = newSize;
}
insert( _bins, _numBins, key, value );
_numElements++;
}
bool lookup(const $1& key,$2& value)
{
unsigned long long index = $3::hash( key ) % _numBins;
if( _bins[index]._isOccupied )
{
entry_$1_$2_$3* ptr = &_bins[index];
do
{
if( $3::equals(ptr->_key,key) )
{
value = ptr->_value;
return true;
}
ptr = ptr->_nextInBin;
}
while( ptr != NULL );
}
return false;
}
};
)
#include <stdlib.h>
namespace MSL
{
generate_vector(int)
generate_vector(PayloadComplex)
generate_hashtable(string,PayloadComplex,PayloadComplexHasher)
generate_hashtable(int,int,intHasher)
}