Ответ 1
Я решил получить шаблонный класс из CMap и CArray и записать файл ArrayMapTempl.h следующим образом:
//Author: Sérgio Loureiro. This is open source.
#include <afxtempl.h>
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
class CArrayMap: protected CArray<KEY,ARG_KEY>, protected CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>
{
private:
bool m_isChangingSize;
public:
CArrayMap():
m_isChangingSize(false)
{
};
CArrayMap(const CArrayMap& src){};
CArrayMap operator=(const CArrayMap& src){return *this;};
// Attributes
INT_PTR GetSize() const;
INT_PTR GetCount() const;
BOOL IsEmpty() const;
INT_PTR GetUpperBound() const;
// Lookup
int Lookup(ARG_KEY key) const;
int Lookup(ARG_KEY key, VALUE& rValue) const;
// Operations
// Clean up
void RemoveAll();
// Accessing elements
const CPair& GetAt(INT_PTR nIndex) const;
CPair& GetAt(INT_PTR nIndex);
void SetAt(INT_PTR nIndex, ARG_KEY newKey, ARG_VALUE newValue);
const CPair& ElementAt(INT_PTR nIndex) const;
CPair& ElementAt(INT_PTR nIndex);
// Direct Access to the element data
const CPair& GetData() const;
CPair& GetData();
// Potentially growing the array
INT_PTR Add(ARG_KEY newKey, ARG_VALUE newValue);
void Copy(const CArrayMap& src);
// overloaded operator helpers
const CPair& operator[](INT_PTR nIndex) const;
CPair& operator[](INT_PTR nIndex);
// Operations that move elements around
BOOL Swap(INT_PTR nIndex, INT_PTR nIndex2);
BOOL MoveUp(INT_PTR nIndex);
BOOL MoveDown(INT_PTR nIndex);
BOOL SwapByKey(ARG_KEY key, ARG_KEY key2);
BOOL MoveUpByKey(ARG_KEY key);
BOOL MoveDownByKey(ARG_KEY key);
BOOL InsertAt(INT_PTR nIndex, ARG_KEY newKey, ARG_VALUE newValue);
BOOL RemoveAt(INT_PTR nIndex);
BOOL RemoveByKey(ARG_KEY key);
public:
void Serialize(CArchive&);
#ifdef _DEBUG
void Dump(CDumpContext&) const;
void AssertValid() const;
#endif
#if 0
public:
// Construction
CArray();
// Attributes
INT_PTR GetSize() const;
INT_PTR GetCount() const;
BOOL IsEmpty() const;
INT_PTR GetUpperBound() const;
void SetSize(INT_PTR nNewSize, INT_PTR nGrowBy = -1);
// Operations
// Clean up
void FreeExtra();
void RemoveAll();
// Accessing elements
const TYPE& GetAt(INT_PTR nIndex) const;
TYPE& GetAt(INT_PTR nIndex);
void SetAt(INT_PTR nIndex, ARG_TYPE newElement);
const TYPE& ElementAt(INT_PTR nIndex) const;
TYPE& ElementAt(INT_PTR nIndex);
// Direct Access to the element data (may return NULL)
const TYPE* GetData() const;
TYPE* GetData();
// Potentially growing the array
void SetAtGrow(INT_PTR nIndex, ARG_TYPE newElement);
INT_PTR Add(ARG_TYPE newElement);
INT_PTR Append(const CArray& src);
void Copy(const CArray& src);
// overloaded operator helpers
const TYPE& operator[](INT_PTR nIndex) const;
TYPE& operator[](INT_PTR nIndex);
// Operations that move elements around
void RemoveAt(INT_PTR nIndex, INT_PTR nCount = 1);
void InsertAt(INT_PTR nStartIndex, CArray* pNewArray);
// Implementation
protected:
TYPE* m_pData; // the actual array of data
INT_PTR m_nSize; // # of elements (upperBound - 1)
INT_PTR m_nMaxSize; // max allocated
INT_PTR m_nGrowBy; // grow amount
public:
~CArray();
void Serialize(CArchive&);
#ifdef _DEBUG
void Dump(CDumpContext&) const;
void AssertValid() const;
#endif
#endif
//----------------------------------------------------------------------------------------
#if 0
public:
// CPair
struct CPair
{
const KEY key;
VALUE value;
protected:
CPair( ARG_KEY keyval ) : key( keyval ) {}
};
protected:
// Association
class CAssoc : public CPair
{
friend class CMap<KEY,ARG_KEY,VALUE,ARG_VALUE>;
CAssoc* pNext;
UINT nHashValue; // needed for efficient iteration
public:
CAssoc( ARG_KEY key ) : CPair( key ) {}
};
public:
// Construction
/* explicit */ CMap(INT_PTR nBlockSize = 10);
// Attributes
// number of elements
INT_PTR GetCount() const;
INT_PTR GetSize() const;
BOOL IsEmpty() const;
// Lookup
BOOL Lookup(ARG_KEY key, VALUE& rValue) const;
const CPair *PLookup(ARG_KEY key) const;
CPair *PLookup(ARG_KEY key);
// Operations
// Lookup and add if not there
VALUE& operator[](ARG_KEY key);
// add a new (key, value) pair
void SetAt(ARG_KEY key, ARG_VALUE newValue);
// removing existing (key, ?) pair
BOOL RemoveKey(ARG_KEY key);
void RemoveAll();
// iterating all (key, value) pairs
POSITION GetStartPosition() const;
const CPair *PGetFirstAssoc() const;
CPair *PGetFirstAssoc();
void GetNextAssoc(POSITION& rNextPosition, KEY& rKey, VALUE& rValue) const;
const CPair *PGetNextAssoc(const CPair *pAssocRet) const;
CPair *PGetNextAssoc(const CPair *pAssocRet);
// advanced features for derived classes
UINT GetHashTableSize() const;
void InitHashTable(UINT hashSize, BOOL bAllocNow = TRUE);
// Implementation
protected:
CAssoc** m_pHashTable;
UINT m_nHashTableSize;
INT_PTR m_nCount;
CAssoc* m_pFreeList;
struct CPlex* m_pBlocks;
INT_PTR m_nBlockSize;
CAssoc* NewAssoc(ARG_KEY key);
void FreeAssoc(CAssoc*);
CAssoc* GetAssocAt(ARG_KEY, UINT&, UINT&) const;
public:
~CMap();
void Serialize(CArchive&);
#ifdef _DEBUG
void Dump(CDumpContext&) const;
void AssertValid() const;
#endif
#endif
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline INT_PTR CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetSize() const
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
return CArray::GetSize();
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline INT_PTR CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetCount() const
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
return CArray::GetCount();
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::IsEmpty() const
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
return CArray::IsEmpty();
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline INT_PTR CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetUpperBound() const
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
return CArray::GetUpperBound();
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline INT_PTR CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Add(ARG_KEY newKey, ARG_VALUE newValue)
{
VALUE rValue;
if( CMap::Lookup(newKey,rValue)) //already exists
return -1;
INT_PTR nIndex = CArray::m_nSize; //old size will be the new position
m_isChangingSize= true;
CMap::operator[] (newKey)= newValue;
CArray::Add(newKey);
m_isChangingSize= false;
ASSERT(CArray::m_nSize == CMap::m_nCount);
return nIndex;
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline const typename CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::CPair&
CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetAt(INT_PTR nIndex) const
{
ASSERT(nIndex >= 0 && nIndex < m_nSize);
if(nIndex >= 0 && nIndex < m_nSize)
{
return *CMap::PLookup(CArray::GetAt(nIndex));
}
AfxThrowInvalidArgException();
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline typename CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::CPair&
CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetAt(INT_PTR nIndex)
{
ASSERT(nIndex >= 0 && nIndex < m_nSize);
if(nIndex >= 0 && nIndex < m_nSize)
{
return *CMap::PLookup(CArray::GetAt(nIndex));
}
AfxThrowInvalidArgException();
};
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
int CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Lookup(ARG_KEY key) const
{
VALUE rValue;
return this->Lookup(key, rValue);
};
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
int CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Lookup(ARG_KEY key, VALUE& rValue) const
{
for (int i=0; i<m_nSize ;i++)
{
if(CArray::operator [] ( i ) == key && CMap::Lookup(key, rValue))
{
return i;
}
}
return -1;
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
void CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::RemoveAll()
{
m_isChangingSize=true;
CMap::RemoveAll();
CArray::RemoveAll();
m_isChangingSize=false;
ASSERT(CArray::m_nSize == CMap::m_nCount);
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Swap(INT_PTR nIndex, INT_PTR nIndex2)
{
if(nIndex<0 || nIndex2<0)
return FALSE;
if(nIndex>=m_nSize || nIndex2>=m_nSize)
return FALSE;
//Swap with itself. Everything is fine and nothing needs to be done
if(nIndex == nIndex2)
return TRUE;
KEY k= CArray::GetAt(nIndex);
CArray::SetAt(nIndex, CArray::GetAt(nIndex2));
CArray::SetAt(nIndex, k);
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::MoveUp(INT_PTR nIndex)
{
if (nIndex == 0)
return FALSE;
return Swap(nIndex,nIndex-1);
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::MoveDown(INT_PTR nIndex)
{
if (nIndex == m_nSize-1)
return FALSE;
return Swap(nIndex,nIndex+1);
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::SwapByKey(ARG_KEY key, ARG_KEY key2)
{
int nIndex= Lookup(key);
int nIndex2= Lookup(key2);
if(nIndex == -1 || nIndex2 == -1)
return FALSE;
return Swap(nIndex,nIndex2);
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::MoveUpByKey(ARG_KEY key)
{
int nIndex= Lookup(key);
if(nIndex == -1)
return FALSE;
return MoveUp(nIndex);
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::MoveDownByKey(ARG_KEY key)
{
int nIndex= Lookup(key);
if(nIndex == -1)
return FALSE;
return MoveDown(nIndex);
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
int CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::InsertAt(INT_PTR nIndex,ARG_KEY newKey, ARG_VALUE newValue)
{
AssertValid(); //ASSERT_VALID(this);
if(nIndex < 0)
return FALSE; //AfxThrowInvalidArgException();
if(nIndex > m_nSize) //doesn't make sense to grow more than last+1 , given newKey has to be unique
return FALSE;
//I am not using this->Lookup(ARG_KEY key), because I
//presume CMap::Lookup(ARG_KEY key, VALUE& rValue) will be faster,
//as it does not need to traverse the array.
VALUE rValue;
if(CMap::Lookup(newKey,rValue)) //already exists
return FALSE;
m_isChangingSize=true;
CMap::operator[] (newKey)= newValue;
CArray::InsertAt(nIndex,newKey,1);
m_isChangingSize=false;
ASSERT(CArray::m_nSize == CMap::m_nCount);
return TRUE;
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::RemoveAt(INT_PTR nIndex)
{
if(nIndex<0 || nIndex>= m_nSize)
return FALSE;
KEY k= CArray::GetAt(nIndex);
//I am not using this->Lookup(ARG_KEY key), because I
//presume CMap::Lookup(ARG_KEY key, VALUE& rValue) will be faster,
//as it does not need to traverse the array.
VALUE rValue;
if(CMap::Lookup(k,rValue)) //already exists
{
m_isChangingSize= true;
CMap::RemoveKey(k);
CArray::RemoveAt(nIndex);
m_isChangingSize= false;
ASSERT(CArray::m_nSize == CMap::m_nCount);
return TRUE;
}
else
return FALSE;
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::RemoveByKey(ARG_KEY key)
{
int nIndex= Lookup(key);
if(nIndex == -1)
return FALSE;
KEY k= CArray::GetAt(nIndex);
return RemoveAt(nIndex);
};
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
void CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Serialize(CArchive& ar)
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
//ASSERT_VALID((const CArray *)this);
//ASSERT_VALID((const CMap *)this);
CObject::Serialize(ar);
if (ar.IsStoring())
{
ar.WriteCount(m_nSize);
if (m_nSize == 0)
return; // nothing more to do
for(INT_PTR i=0;i<m_nSize;i++)
{
KEY* pKey;
VALUE* pValue;
/*
* in some cases the & operator might be overloaded, and we cannot use it to
* obtain the address of a given object. We then use the following trick to
* get the address
*/
pKey = reinterpret_cast< KEY* >( &reinterpret_cast< int& >( const_cast< KEY& > ( static_cast< const KEY& >( CArray::operator[]( i ) ) ) ) );
pValue = reinterpret_cast< VALUE* >( &reinterpret_cast< int& >( static_cast< VALUE& >( CMap::operator[]( *pKey ) ) ) );
SerializeElements<KEY>(ar, pKey, 1);
SerializeElements<VALUE>(ar, pValue, 1);
}
}
else
{
DWORD_PTR nNewCount = ar.ReadCount();
while (nNewCount--)
{
KEY newKey[1];
VALUE newValue[1];
SerializeElements<KEY>(ar, newKey, 1);
SerializeElements<VALUE>(ar, newValue, 1);
this->Add(newKey[0], newValue[0]); //includes checking if it already exists
}
}
}
#ifdef _DEBUG
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
void CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Dump(CDumpContext& dc) const
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
CObject::Dump(dc);
dc << "with " << m_nSize << " elements";
if (dc.GetDepth() > 0)
{
// Dump in format "[key] -> value"
KEY key[1];
VALUE val[1];
POSITION pos = GetStartPosition();
while (pos != NULL)
for (int i=0; i<m_nSize; i++)
{
key[0]= CArray::operator[]( i );
//val[0]= CMap::operator[]( key[0] );
CMap::Lookup(key[0], val[0]);
dc << "\n\t[";
DumpElements<KEY>(dc, key, 1);
dc << "] = ";
DumpElements<VALUE>(dc, val, 1);
}
}
dc << "\n";
}
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
void CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::AssertValid() const
{
CArray::AssertValid();
CMap::AssertValid();
if(!m_isChangingSize)
ASSERT(CArray::m_nSize == CMap::m_nCount);
}
#endif //_DEBUG
Теперь у меня есть все, что мне нужно. Я не тестировал все, но я считаю, что при необходимости потребуется лишь небольшая коррекция.
В любом случае, спасибо всем за ответы и комментарии.