MFC dictionary collection with key unicity and ordering by position

前端 未结 5 1608
深忆病人
深忆病人 2021-01-26 15:18

Looking at table on http://msdn.microsoft.com/en-us/library/y1z022s1%28v=vs.80%29.aspx#_core_collection_shape_features

I can not see a MFC collection for the purpose I n

相关标签:
5条回答
  • 2021-01-26 15:35

    I decided to derive a templated class from both CMap and CArray and write the ArrayMapTempl.h file as follows:

    //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
    

    Now I have everything I need. I didn't test everything, but I believe that only little corrections will be needed, if needed.

    Anyway, thank you all for the answers and comments.

    0 讨论(0)
  • 2021-01-26 15:38

    After this conversation on C++ Freenode's IRC channel, I feel tempted to try Boost Multi index (zxvf is me):

    zxvf            hello
    zxvf            I was trying to solve the problem of this question: http://stackoverflow.com/questions/9329011/mfc-dictionary-collection-with-key-unicity-and-ordering-by-position/9438561#9438561
    zxvf            what kind of container would you recommend to me?
    TinoDidriksen   std::vector sounds fitting.
    zxvf            you can not guarantee unicity of keyEnum, TinoDidriksen
    MrBar       zxvf: so use std::map then
    zxvf            MrBar: I think you all didn't get the point of the problem
    evilissimo  I think you'd need something like boost multi_index
    zxvf            I want both unicity of KeyEnum and OrderIndex 
    MrBar       zxvf: i get point, but that container u asked about not exist
    TinoDidriksen   zxvf, Boost.Multi_index then.
    evilissimo  zxvf, by unicity you mean that there are at no given time two of any of them in the container?
    evilissimo  or by the combination?
    MrBar       zxvf: u need to use intermix of containers
    zxvf            and NO, I don't want unicity of the pair <OrderIndex, KeyEnum> but unicity of each one
    evilissimo  zxvf, had to ask
                    zxvf seeing what the heck is Boost Multi index
    MrBar       zxvf: noooo, not see in boost there no back way
    zxvf            MrBar, could you rephrase please?
    zxvf            Not english native speaker, me
    evilissimo  zxvf, that wasn't even valid english
    MrBar       zxvf: hmm i too
    evilissimo  zxvf, basically there are tons of useful stuff in boost, and usually once you start loving one library you start using more of them 
    zxvf            thanks, people. It's aready late here and I will not see this tonight. But seems to me that Boost Multi index is a very good help for me
    
    0 讨论(0)
  • 2021-01-26 15:52

    Use an array and store the index in the array as an element of your value. Use another CMap for fast hash access if you need. The maps i know and heared off all d o not support an Order Index number.

    0 讨论(0)
  • 2021-01-26 16:02

    MFC doesn't provide a container that does what you need, but you can use std::map or std::set instead. Each will keep the keys unique; which you choose will depend on whether you want your key separated from the payload or integrated into a single object.

    0 讨论(0)
  • 2021-01-26 16:02

    When you say 'Ordered' you are already indirectly implying that 'order index' is the 'key' which is unique. Why can't you use std::vector?

    If you want to have another unique key than you can use std::map but you will have to improvise 'order' as part of the key/value combination. I see the ultimate solution will be something composite. You will have your own class to support what you want which in tern may std::map to implement this or any other contain like list.

    0 讨论(0)
提交回复
热议问题