c++mfcstdmapclistbox

Setting std::map items as itemdata of CListBox


I have a similar question here but the context of this new question is different.

Background

I have this variable: PublisherMap m_mapPublishers;

The definition of PublisherMap is:

using PublisherMap = std::map<CString, S_DEMO_ENTRY_EX>;

The code

I have this method that reads the map and populates a CListBox:

bool CChristianLifeMinistryPersonalCopiesDlg::InitPublishersGrid()
{
    try
    {
        m_lbPublishers.ResetContent();

        for (auto & mapPublisher : m_mapPublishers)
        {
            bool bInclude = false;

            if (m_iDisplayMode == DISPLAY_EVERYONE)
                bInclude = true;
            else if (m_iDisplayMode == DISPLAY_BROTHER && mapPublisher.second.eGender == GENDER_MALE)
                bInclude = true;
            else if (m_iDisplayMode == DISPLAY_SISTER && mapPublisher.second.eGender == GENDER_FEMALE)
                bInclude = true;

            if (bInclude && m_bLimitDisplay)
            {
                CString strTemp;
                if (!m_mapSSAssignedPublishers.Lookup(mapPublisher.first, strTemp))
                    bInclude = FALSE;
            }

            if (bInclude)
            {
                int i = m_lbPublishers.AddString(mapPublisher.first);
                m_lbPublishers.SetItemData(i, MAKEWPARAM(mapPublisher.second.eGender, mapPublisher.second.eAppointed));

            }
        }
    }
    catch (_com_error e)
    {
        LPCTSTR szError = e.ErrorMessage();
        AfxMessageBox(szError);
        return false;
    }
    catch (CException* e)
    {
        e->Delete();
        AfxMessageBox(_T("CException"));
        return false;
    }

    m_iSelectMode = SELECT_NONE;
    UpdateData(FALSE);

    return true;
}

Notice that I use item data:

m_lbPublishers.SetItemData(i, 
    MAKEWPARAM(mapPublisher.second.eGender, mapPublisher.second.eAppointed));

It works absolutely fine. If I was using a CPtrArray I would have assigned the actual structure object pointers against each entry in the list box.

The question

I don't know the mechanics of std::map enough. Is there any safe way to directly associate each entry from the map (mapPublisher) against each list box entry so that I can later access it?

I realise I could take the text of the list box entry and then find it in the map and get it that way. But if there is a more direct way to tie the two together?


Solution

  • std::map is specified as an associative container that never moves existing elements, see [associative.reqmts]/9:

    The insert and emplace members shall not affect the validity of iterators and references to the container, and the erase members shall invalidate only iterators and references to the erased elements.

    In practice it's often implemented as a red-black tree.

    So it is safe to keep pointers to existing elements, as long as their lifetime exceeds the lifetime of the pointers.

    Note you will lose that guarantee if you switch to std::unordered_map (a hash map).


    To set:

        m_lbPublishers.SetItemDataPtr(i, &mapPublisher.second);
    

    To retrieve:

        auto psEntry = (S_DEMO_ENTRY_EX*)m_lbPublishers.GetItemDataPtr(i);
    

    CListBox::GetItemDataPtr() returns void* so a cast is required.