CMAP under vs2005+
This is a multi-part message in MIME format.
--------------070202080900080202070601
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Hi,
Under VC6, I had a wrapper CMAP template, which I called CMapEx which
I used for many years as a way to map case insensitive key strings to
objects.
In other words, using an example with the traditional CMap class
collection:
CMap<CString, const char *,CString, const char *> map;
map["key1"] = "ext 2223";
map["KEY1"] = "ext 2223";
map["kEy2"] = "ext 2225";
map["Key2"] = "ext 2225";
However, this is four unique keys. So CMapEx allowed me to do this in
a case insensitive manner:
CMapEx<CString, const char *> map;
map["key1"] = "ext 2223";
map["KEY1"] = "ext 2223"; // Overrides first key1
map["kEy2"] = "ext 2225";
map["Key2"] = "ext 2225"; // Overrides first kEy2
There would be only two keys here.
CMapEx was been extremely valuable in my software compiled under VC6,
and now that I am moving to VS2005, of course, I am coming across some
afx template compatibility issues.
I guess I would like to ask if A) is it possible with a compiler
define or something that make afxtempl.h behavior the same? or B) is
there is a alternative approach, maybe using std c/c++ template that
will give me the same case insensitive collection behavior?
CMapEx (mapex.h) was designed using vc6.0 afxtempl.h as a starting
point and I attempted to migrated the vc8.0 afxtempl.h differences,
but the vc8 compiler is giving me template errors with the
implementation, i.e,, like with one of the assignments above. So
maybe I am expecting too much here and should probably CMapEx based on
afxtempl.h for a more reliable version not dependend on afxtempl.h.
If is possible to use CMap with an unique HashKey function unique to
CMapEx wrapper for CMAP so I can make the hash case insensitive?
Any tips?
I attached mapex.h. Maybe the changes necessary are more simple than I
know off hand.
--
--------------070202080900080202070601
Content-Type: text/plain;
name="mapex.h"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="mapex.h"
//---------------------------------------------------------------------
// File : mapex.h
// About:
//
// A extended CMap template specifically for using case-insensitive
// string keys. No need to force the keys to one case. Any objects
// can be used for the map value parameter.
//
// Usage:
//
// CMapEx<VALUE, ARG_VALUE> object;
//
// Example:
//
// CMapEx<CString, const char *> map;
//
// map["key1"] = "ext 2223";
// map["KEY1"] = "ext 2223"; << overrides the first "key1"
// map["kEy2"] = "ext 2225";
// map["Key2"] = "ext 2225"; << override the first "kEy2"
//
// printf("count: %d\n",map.GetCount()); // SHOULD BE 2
//
//
// Reference:
//
// v:\vstudio\vc98\mfc\include\afx.h
// v:\vstudio\vc98\mfc\include\afxtempl.h
//
//---------------------------------------------------------------------
#ifndef __MAP_EX_H__
#define __MAP_EX_H__
#ifndef __AFXTEMPL_H__
#include <afxtempl.h>
#endif
////////////////////////////////////////////////////////////////////
// CMapEx class uses the CMap template with string based keys.
//
#define TKEYSTR CString
#define TKEYARG LPCTSTR
template<class VALUE, class ARG_VALUE>
class CMapEx : public CMap<TKEYSTR, TKEYARG, VALUE, ARG_VALUE>
{
typedef CMap inherited;
public:
CMapEx(int nBlockSize = 10);
CMapEx(const CMapEx &x);
private:
// new variables
POSITION pos;
public:
// new function:
BOOL First(TKEYSTR &key, VALUE &rValue)
{
pos = GetStartPosition();
if (!pos) return FALSE;
GetNextAssoc(pos, key, rValue);
return TRUE;
}
BOOL Next(TKEYSTR &key, VALUE &rValue)
{
if (!pos) return FALSE;
GetNextAssoc(pos, key, rValue);
return TRUE;
}
DWORD GetSize() { return GetCount(); } // 451.2
// Overriding Public Members
public:
void SetAt(TKEYARG key, ARG_VALUE newValue);
VALUE& operator[](TKEYARG key);
void GetNextAssoc(POSITION& rNextPosition, CString& rKey, VALUE& rValue) const;
BOOL Lookup(TKEYARG key, VALUE& rValue) const;
BOOL RemoveKey(TKEYARG key);
// Overriding Protected members
protected:
CAssoc* NewAssoc();
void FreeAssoc(CAssoc*);
CAssoc* GetAssocAt(TKEYARG, UINT&) const;
// Private internal members
private:
UINT HashString(TKEYARG key) const;
BOOL CompareKeys(TKEYARG key1, TKEYARG key2) const;
// Protected internal members
protected:
};
//--------------------------------------------------------------------------
template<class VALUE, class ARG_VALUE>
inline CMapEx<VALUE, ARG_VALUE>::CMapEx(int nBlockSize)
:CMap<TKEYSTR, TKEYARG, VALUE, ARG_VALUE>(nBlockSize)
{
pos = NULL;
}
template<class VALUE, class ARG_VALUE>
inline CMapEx<VALUE, ARG_VALUE>::CMapEx(const CMapEx &x)
{
*this = x;
pos = NULL;
}
template<class VALUE, class ARG_VALUE>
AFX_INLINE void CMapEx<VALUE, ARG_VALUE>::SetAt(TKEYARG key, ARG_VALUE newValue)
{
(*this)[key] = newValue;
}
template<class VALUE, class ARG_VALUE>
VALUE& CMapEx<VALUE, ARG_VALUE>::operator[](TKEYARG key)
{
ASSERT_VALID(this);
UINT nHash;
CAssoc* pAssoc;
if ((pAssoc = GetAssocAt(key, nHash)) == NULL)
{
if (m_pHashTable == NULL)
InitHashTable(m_nHashTableSize);
// it doesn't exist, add a new Association
pAssoc = NewAssoc();
pAssoc->nHashValue = nHash;
pAssoc->key = key;
// 'pAssoc->value' is a constructed object, nothing more
// put into hash table
pAssoc->pNext = m_pHashTable[nHash];
m_pHashTable[nHash] = pAssoc;
}
return pAssoc->value; // return new reference
}
template<class VALUE, class ARG_VALUE>
CMapEx<VALUE, ARG_VALUE>::CAssoc*
CMapEx<VALUE, ARG_VALUE>::NewAssoc()
{
if (m_pFreeList == NULL)
{
// add another block
CPlex* newBlock = CPlex::Create(m_pBlocks, m_nBlockSize, sizeof(CMapEx::CAssoc));
// chain them into free list
CMapEx::CAssoc* pAssoc = (CMapEx::CAssoc*) newBlock->data();
// free in reverse order to make it easier to debug
pAssoc += m_nBlockSize - 1;
for (int i = m_nBlockSize-1; i >= 0; i--, pAssoc--)
{
pAssoc->pNext = m_pFreeList;
m_pFreeList = pAssoc;
}
}
ASSERT(m_pFreeList != NULL); // we must have something
CMapEx::CAssoc* pAssoc = m_pFreeList;
m_pFreeList = m_pFreeList->pNext;
m_nCount++;
ASSERT(m_nCount > 0); // make sure we don't overflow
ConstructElements(&pAssoc->key, 1);
ConstructElements<VALUE>(&pAssoc->value, 1); // special construct values
return pAssoc;
}
template<class VALUE, class ARG_VALUE>
void CMapEx<VALUE, ARG_VALUE>::FreeAssoc(CMapEx::CAssoc* pAssoc)
{
DestructElements<VALUE>(&pAssoc->value, 1);
DestructElements(&pAssoc->key, 1);
pAssoc->pNext = m_pFreeList;
m_pFreeList = pAssoc;
m_nCount--;
ASSERT(m_nCount >= 0); // make sure we don't underflow
// if no more elements, cleanup completely
if (m_nCount == 0)
RemoveAll();
}
template<class VALUE, class ARG_VALUE>
CMapEx<VALUE, ARG_VALUE>::CAssoc*
CMapEx<VALUE, ARG_VALUE>::GetAssocAt(TKEYARG key, UINT& nHash) const
{
nHash = HashString(key) % m_nHashTableSize;
if (m_pHashTable == 0) return 0;
CAssoc* pAssoc;
for (pAssoc = m_pHashTable[nHash]; pAssoc != 0; pAssoc = pAssoc->pNext)
{
if (CompareKeys(pAssoc->key, key))
return pAssoc;
}
return 0;
}
template<class VALUE, class ARG_VALUE>
BOOL CMapEx<VALUE, ARG_VALUE>::Lookup(TKEYARG key, VALUE& rValue) const
{
ASSERT_VALID(this);
UINT nHash;
CAssoc* pAssoc = GetAssocAt(key, nHash);
if (pAssoc == NULL)
return FALSE; // not in map
rValue = pAssoc->value;
return TRUE;
}
template<class VALUE, class ARG_VALUE>
BOOL CMapEx<VALUE, ARG_VALUE>::RemoveKey(TKEYARG key)
// remove key - return TRUE if removed
{
ASSERT_VALID(this);
if (m_pHashTable == NULL)
return FALSE; // nothing in the table
CAssoc** ppAssocPrev;
ppAssocPrev = &m_pHashTable[HashString(key) % m_nHashTableSize];
CAssoc* pAssoc;
for (pAssoc = *ppAssocPrev; pAssoc != NULL; pAssoc = pAssoc->pNext)
{
if (CompareKeys(pAssoc->key, key))
{
// remove it
*ppAssocPrev = pAssoc->pNext; // remove from list
FreeAssoc(pAssoc);
return TRUE;
}
ppAssocPrev = &pAssoc->pNext;
}
return FALSE; // not found
}
template<class VALUE, class ARG_VALUE>
void CMapEx<VALUE, ARG_VALUE>::GetNextAssoc(POSITION& rNextPosition,
CString& rKey, VALUE& rValue) const
{
ASSERT_VALID(this);
ASSERT(m_pHashTable != NULL); // never call on empty map
CAssoc* pAssocRet = (CAssoc*)rNextPosition;
ASSERT(pAssocRet != NULL);
if (pAssocRet == (CAssoc*) BEFORE_START_POSITION)
{
// find the first association
for (UINT nBucket = 0; nBucket < m_nHashTableSize; nBucket++)
if ((pAssocRet = m_pHashTable[nBucket]) != NULL)
break;
ASSERT(pAssocRet != NULL); // must find something
}
// find next association
ASSERT(AfxIsValidAddress(pAssocRet, sizeof(CAssoc)));
CAssoc* pAssocNext;
if ((pAssocNext = pAssocRet->pNext) == NULL)
{
// go to next bucket
for (UINT nBucket = pAssocRet->nHashValue + 1;
nBucket < m_nHashTableSize; nBucket++)
if ((pAssocNext = m_pHashTable[nBucket]) != NULL)
break;
}
rNextPosition = (POSITION) pAssocNext;
// fill in return data
rKey = pAssocRet->key;
rValue = pAssocRet->value;
}
template<class VALUE, class ARG_VALUE>
AFX_INLINE UINT CMapEx<VALUE, ARG_VALUE>::HashString(TKEYARG Key) const
{
LPCSTR key = Key;
UINT nHash = 0;
while (*key) {
nHash = (nHash<<5) + nHash + towlower(*key++);
}
return nHash;
}
template<class VALUE, class ARG_VALUE>
AFX_INLINE BOOL CMapEx<VALUE, ARG_VALUE>::CompareKeys(TKEYARG key1, TKEYARG key2) const
{
return stricmp(key1,key2) == 0;
}
//------------------------------------------------------------------------
typedef CMapEx<BOOL, BOOL> CMapBoolEx;
typedef CMapEx<DWORD, DWORD> CMapDWordEx;
typedef CMapEx<CString, const char *> CMapStringEx;
#endif
--------------070202080900080202070601--