//+----------------------------------------------------------------------- // // Template Array Implementation // Copyright (C) Microsoft Corporation, 1996, 1997 // // File: tarray.h // // Contents: Template for resizeable arrays. Allows for the creation // and manipulation of arrays of any type. Arrays can // be dynamically reallocated, and thus can "grow and shrink". // Constructors and destructors of array elements is // automatically handles, even when the size of the array is // changed. // Templates: TSTDArray // //------------------------------------------------------------------------ #ifndef _TARRAY_H_ #define _TARRAY_H_ //+----------------------------------------------------------------------- // // Class: TSTDArray // // Synopsis: Contains an array of "type". Allows array to grow // dynamically. During debug, can check bounds on indices. // Array is indexed 0 to _cArraySize-1. _cArraySize holds // number of elements. // // Methods: Init allocate memory for array // Passivate // [] allows indexing of array // GetSize returns size of array // InsertElems insert elements anywhere in array // DeleteElems delete elements anywhere in array // //------------------------------------------------------------------------ template class TSTDArray { public: TSTDArray(); #if DBG == 1 ~TSTDArray(); #endif HRESULT Init(const size_t cSize); // initialize data structures void Passivate(); TYPE& operator[](const size_t iElement); const TYPE& operator[](const size_t iElement) const; // constant reference size_t GetSize() const { return _cArraySize; } HRESULT InsertElems(const size_t iElem, const size_t cElems); void DeleteElems(const size_t iElem, const size_t cElems); private: #if DBG == 1 void IsValidObject() const; #else void IsValidObject() const { } #endif // All elements are packaged inside a class CElem. This allows us to // overload the new operator so that we can manually invoke the // constructors. class CElem { friend TSTDArray; private: // Now we overload the new operator to allow placement argument void *operator new(size_t uSize, void *pv) { return pv; } // Internal data: TYPE _Element; // actual element }; // Internal data: CElem *_paArray; // pointer to actual data size_t _cArraySize; size_t _cAllocSize; // the size of allocated object #if DBG == 1 // Ensure we call constructors and destructors right number of times. // Used only as a check. size_t _cNumElems; #endif }; //+--------------------------------------------------------------------------- // // Member: IsValidObject // // Synopsis: Validation method. Checks that array structure is valid. // It is usefull to call this member function at the beginning // of each member function that uses the internal array to // ensure that the array is not corrupt before attempting to // modify it. // #if DBG == 1 template void TSTDArray::IsValidObject() const { _ASSERT("Must have valid this pointer" && this ); _ASSERT("Array has no memory" && _paArray ); _ASSERT("destructors called wrong number of times" && (_cNumElems == _cArraySize) ); } #endif //+----------------------------------------------------------------------- // // Constructor for TSTDArray // // Synopsis: Doesn't do anything. Must call member function init to // actually initialize. Only call init once. // // Arguments: None. // // Returns: Nothing. // template TSTDArray::TSTDArray() { // We null the internal data, so that they are not actually used // until the init member function is called. _paArray = 0; _cArraySize = 0; _cAllocSize = 0; #if DBG == 1 _cNumElems = 0; #endif } //+----------------------------------------------------------------------- // // Destructor for TSTDArray // // Synopsis: Must call member function passivate // to actually de-initialize. // // Arguments: None. // // Returns: Nothing. // #if DBG == 1 template TSTDArray::~TSTDArray() { _ASSERT("Passivate must be called first" && !_paArray ); _ASSERT("Destructors called wrong number of times" && (_cNumElems == 0) ); } #endif //+----------------------------------------------------------------------- // // Member: Init // // Synopsis: Initializes the array abstract data type. Allocates // memory for the array. Also sets the cArraySize to // the number of elements. // // Arguments: cSize initial size of array (# of elements) // // Returns: Success if memory can be allocated for table. // Returns E_OUTOFMEMORY if can't get memory. // template HRESULT TSTDArray::Init(const size_t cSize) { HRESULT hr; _ASSERT(this); _ASSERT("Only call init once" && !_paArray ); // Get memory: // 0 element array is made into 1 element array so that it functions // normally. { //;begin_internal // BUGBUG: // MSVC 2.0 has a bug in it. Evaluating the expression sizeof(CElem) // seems to confuse it. CElem is a class containing a variable // whose size can only be calculated when the template containing it // is instantiated. In addition to this, CElem is a member of that // template. Placing the sizeof(CElem) expression within a more // complicated expression is not possible. //;end_internal size_t uCElemSize; uCElemSize = sizeof(CElem); uCElemSize *= (cSize == 0 ? 1 : cSize); _paArray = (CElem *) CoTaskMemAlloc(uCElemSize); } if (!_paArray) { hr = E_OUTOFMEMORY; } else { CElem *pTemp; // index used to call constructors // We need to call the constructors manually for each element: for (pTemp = _paArray; pTemp < _paArray + cSize; pTemp++) { new (pTemp) CElem; #if DBG == 1 _cNumElems++; #endif } _cArraySize = cSize; _cAllocSize = (cSize == 0 ? 1 : cSize); hr = S_OK; } return hr; } //+----------------------------------------------------------------------- // // Member: Passivate // // Synopsis: Releases memory held in array. Should be called before // the object is destroyed. Should only be called once on an // object. // // Arguments: None. // // Returns: Nothing. // //------------------------------------------------------------------------ template void TSTDArray::Passivate() { IsValidObject(); _ASSERT("Only call Passivate once" && _paArray ); // We need to call the destructors manually for each element: { CElem *pTemp; // index used to call destructors for (pTemp = _paArray; pTemp < _paArray + _cArraySize; pTemp++) { pTemp->CElem::~CElem(); #if DBG == 1 _cNumElems--; #endif } } CoTaskMemFree(_paArray); _paArray = 0; // make sure we don't call Passivate again _cArraySize = 0; _cAllocSize = 0; } //+----------------------------------------------------------------------- // // Member: operator[] // // Synopsis: Allows indexing of array's elements. Use this to either // store an element in the array or read an element from // the array. It is the user's responsibility to ensure // that the index is within the proper range, 0.._cArraySize-1. // During debugging, the index range is checked. // // Arguments: iElement Element index // // Returns: Reference to element. // template inline TYPE& TSTDArray::operator[](const size_t iElement) { IsValidObject(); _ASSERT("Index is out of range" && (iElement < _cArraySize) ); return _paArray[iElement]._Element; } //+----------------------------------------------------------------------- // // Member: operator[] const // // Synopsis: Same as previous [] operator, but returns a constant // reference so that it can't be used as an l-value. // // Arguments: iElement Element index // // Returns: Constant reference to element. // template inline const TYPE& TSTDArray::operator[](const size_t iElement) const { IsValidObject(this); _ASSERT("Index is out of range" && (iElement < _cArraySize) ); return _paArray[iElement]._Element; } //+----------------------------------------------------------------------- // // Member: InsertElems // // Synopsis: Changes the size of the array by using MemRealloc(). // Inserts a number of elements cElems into the array at // iElem. This can be used to add new elements to the end of // the array by specifying iElem equal to _cArraySize. It is // the responsibility of the user to make sure that iElem is // within the proper bounds of the array, although this will // be checked during debug mode. // // Arguments: iElem place to insert first element // cElems number of new elements // // Returns: Returns success if elements can be added. // Returns E_OUTOFMEMORY if request cannot be met. // Array retains its old size if the request cannot be met. // template HRESULT TSTDArray::InsertElems(const size_t iElem, const size_t cElems) { HRESULT hr = S_OK; // Note that you can insert past the END of an array (appending to it): _ASSERT("iElem is too large" && (iElem <= _cArraySize) ); if (_cArraySize + cElems > _cAllocSize) { // Resize the current array we have: ULONG cAllocSize = _cAllocSize ? _cAllocSize : 8; CElem * paArray; // new array // Double alloc size until it's big enough. This will, I suppose, loop // forever if someone asks to allocate more than 2^31 elements. _ASSERT(_cArraySize + cElems < MAXLONG); while (_cArraySize + cElems > cAllocSize) cAllocSize <<= 1; paArray = (CElem *)CoTaskMemRealloc(_paArray, sizeof(CElem) * cAllocSize); if (!paArray) { hr = E_OUTOFMEMORY; goto Error; } _paArray = paArray; _cAllocSize = cAllocSize; } IsValidObject(); // Now we have to shift elements to allow space for the new elements: memmove(_paArray + iElem + cElems, // dest _paArray + iElem, (_cArraySize - iElem) * sizeof(CElem)); // Call constructors on all new elements: { CElem *pTemp; // index used to call constructors for (pTemp = _paArray + iElem; pTemp < _paArray + iElem + cElems; pTemp++) { new (pTemp) CElem; #if DBG == 1 _cNumElems++; #endif } } _cArraySize += cElems; Error: return hr; } //+----------------------------------------------------------------------- // // Member: DeleteElems // // Synopsis: Deletes a number of elements cElems from the array at // iElem. It is the responsibility of the user to make sure // that the region to be deleted is within the proper bounds // of the array, although this will be checked during // debug mode. // // Arguments: iElem place to delete first element // cElems number of elements to delete // // Returns: Returns success. // template void TSTDArray::DeleteElems(const size_t iElem, const size_t cElems) { IsValidObject(); _ASSERT("Region to delete is too large" && (iElem+cElems-1 < _cArraySize) ); // First we need to call destructors on elements: { CElem *pTemp; // index used to call destructors for (pTemp = _paArray + iElem; pTemp < _paArray + iElem + cElems; pTemp++) { pTemp->CElem::~CElem(); #if DBG == 1 _cNumElems--; #endif } } // Now we need to shift the remaining elements in: memmove(_paArray + iElem, // dest _paArray + iElem + cElems, (_cArraySize - (iElem + cElems)) * sizeof(CElem)); _cArraySize -= cElems; } #endif // _TARRAY_H_