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?
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.
As long as the node of the map isn't destroyed/deleted you can pass a pointer to the mapped datatype directly to the CListBox::SetItemDataPtr.
So in your case accessing the S_DEMO_ENTRY_EX and using a pointer using &mapPublisher.second is OK.
This is guaranteed by the rules for the STL
Related
I have a factory function that returns a non-owning pointer to the created object after it is inserted into a resource-owning map to be later stored as a non-owning pointer elsewhere; however, by using the return value of std::map::try_emplace which should be an iterator to what was/is inserted, this causes the internal contents of the std::unique_ptr to be invalid when accessed through the referring non-owning pointer.
Instead, I have to call std::map::find and basically search for a value twice.
I don't quite understand what's wrong with the non-working version of the code below.
Shouldn't the return value of std::map::try_emplace be directly usable/copyable?
What I wanted to do, but doesn't work:
std::map<std::string, std::unique_ptr<Feature>> Feature::s_registry{};
Feature* Feature::CreateFeature(Map* map, const XMLElement& elem) {
auto new_feature = std::make_unique<Feature>(map, elem);
//Copy name out so it isn't erased via the move operation.
std::string new_feature_name = new_feature->name;
if(auto&& [where, inserted] = s_registry.try_emplace(new_feature_name, std::move(new_feature)); inserted) {
return where->second.get();
} else {
if(where != std::end(s_registry)) {
return where->second.get();
}
}
return nullptr;
}
Instead I have to call find to get a valid iterator:
std::map<std::string, std::unique_ptr<Feature>> Feature::s_registry{};
Feature* Feature::CreateFeature(Map* map, const XMLElement& elem) {
auto new_feature = std::make_unique<Feature>(map, elem);
//Copy name out so it isn't erased via the move operation.
std::string new_feature_name = new_feature->name;
if(const auto where_inserted = s_registry.try_emplace(new_feature_name, std::move(new_feature)); where_inserted.second) {
if(const auto found = s_registry.find(new_feature_name); found != std::end(s_registry)) {
return found->second.get();
}
} else {
if(const auto found = s_registry.find(new_feature_name); found != std::end(s_registry)) {
return found->second.get();
}
}
return nullptr;
}
The code can be as simple as
Feature* Feature::CreateFeature(Map* map, const XMLElement& elem)
{
auto new_feature = std::make_unique<Feature>(map, elem);
return s_registry.emplace(new_feature->name, std::move(new_feature)).first->second.get();
}
If the new_feature was not inserted because the slot is already occupied, .first points to the already existing value. Otherwise, it points to the newly inserted object. In both cases, that object's ->second should be valid.
Furthermore, std::move does not move anything. The members that new_feature points to can be used until the final destination is initialized, at which point the std::map will already know where to insert the value. Therefore, it is not necessary to keep new_feature->name in a separate value. There is some discussion of this behavior in this post.
Are there any methods of procuring an iterator, when working with a Standard Library map container, which don't require searching throughout the container?
I have a managing class for a map, and I wish to return the iterator associated to items added to the map. I don't want to rely upon find() if at all possible. If I can avoid searches I figure all the better.
std::map<char, bool>::iterator ClassA::Add(char item)
{
mymap[item] = false;
return mymap.get_iterator_lastitem();
}
Perhaps
return mymap.end() - 1;
If you're not using C++11, then
std::map<char, bool>::iterator ClassA::Add(char item)
{
std::pair<std::map<char, bool>::iterator, bool> result = mymap.insert(std::make_pair(item, false));
if(!result.second) {
// Item already exists, modify that existing item
result.first->second = false;
}
return result.first;
}
If you are using C++11 then it is better to use emplace + auto.
std::map<char, bool>::iterator ClassA::Add(char item)
{
auto result = mymap.emplace(item, false);
if(!result.second) {
// Item already exists, modify that existing item
result.first->second = false;
}
return result.first;
}
Live example
Both insert and emplace return a pair of an iterator and a boolean, of which the iterator points to the inserted or existing element and the boolean indicates whether an insertion (true) took place or if not (false) of which the returned iterator points to the already-existing element with the key.
I was wondering if there is a way in C++ to know what called a function? Like the this keyword in Java or JavaScript.
For example, I have a function called insert, which inserts an item into a linked list, I want the linked-list that called those the function insert to call two other functions. How would I do that?
I have this right now, is this valid?
bool linked_list::insert( int i )
{
bool inserted = false;
if( this.is_present(i) ) /* function is_present is defined earlier checks if an int is already in the linked-list. */
{
inserted = true // already inside the linked-list
}
else
{
this.Put( 0, i ); /* function Put is defined earlier and puts an int in a linked-list at a given position (first param.). */
inserted = true; // it was put it.
}
return inserted;
}
For historical reasons, this is a pointer. Use -> instead of ..
bool linked_list::insert(int i) {
bool inserted = false;
if(this->is_present(i)) {
inserted = true; // fixed syntax error while I was at it.
} else {
this->put(0, i); // fixed inconsistent naming while I was at it.
inserted = true;
}
return inserted;
}
Usually it is not needed to use this-> at all; you can just do if(is_present(i)).
this works in c++ the same as it does in Java. The only difference is that you need to use this-> instead of this. this is a pointer than therefor you cannot use the dot operator to access it's members.
why don't you just call the other functions in linked_list::insert(int)? And no, it is not valid, you should put this -> something instead of this.something
My application merges two std::map instances. If there are no duplicates, the merge completes without intervention. However, if a duplicate is detected, then the method asks whether the new value should be ignored or overwritten. (This query may be answered by a rule table, message box to the user, or some other logic ... it is just an instance of a class derived from a pure virtual class with a bool confirm() const method.)
If the insert fails, and they decide to overwrite the existing entry, I already have an iterator pointing to the correct item to update. Can I use this iterator to update the value directly, or do I have to call operator[] and take the hit of another lookup?
// typedef std::map<Foo, Foo, Compare> Dictionary;
// Dictionary this_dictionary, other_dictionary;
for (Dictionary::const_iterator i = other_dictionary.begin();
i != other_dictionary.end();
++i) {
std::pair<Dictionary::iterator,bool> ret = this_dictionary.insert(*i);
if (!ret.second && confirmer.confirm()) {
// ???
}
}
You need to use Dictionary::iterator instead of Dictionary::const_iterator in the return from insert.
for (Dictionary::const_iterator i = other_dictionary.begin();
i != other_dictionary.end();
++i) {
// Use standard iterator here
std::pair<Dictionary::iterator,bool> ret = this_dictionary.insert(*i);
if (!ret.second && confirmer.confirm()) {
ret.first->second = i->first;
}
}
You can, but you need to use iterator instead of const_iterator.
In my program I want to use view/model pattern with view = QListView and my own model which I subclassed from QAbstractListModel. My data class looks like
class Avtomat
{
...
map<QString, State *> states;
...
};
In my model class
class AvtomatModel : public QAbstractListModel
{
...
Avtomat a;
...
};
I'm trying to overload QAbstractItemView::index function so that I'm able to provide interface for editing data map.
As index function takes int row argument I solved that problem by providing the following
State* Avtomat::pStateFromIndex(int index) const
{
map<QString, State *>::const_iterator i;
int count = 0;
for (i = states.begin(); i != states.end() && count != index; ++i)
++count;
return (*i).second;
}
so in my index function I do like this
return createIndex(row, column, a.pStateFromIndex(row));
but that seems pretty ugly because I have O(n). Can you help me to design a better way to access my map using int index?
This is a fundamental data modelling issue. What's the primary way you need to retrieve your data? By key or by index?
If you only ever access it by index (including in the model) then you're simply using an inappropriate data structure and should switch to something else like a list.
If you do need to query by key too then you have several options. There's nothing wrong with what you're doing already if efficiency isn't a huge driver, especially if the data set is small. Alternatively you could also maintain both key and index mappings to your underlying data. This is a simple and effective solution but it means you have to take the hit of managing consistency between the two and has a memory overhead which may be significant if your data set is large. Or you could use a data structure that provides access by both key and index directly. Ultimately it depends on your specific circumstances and the data domain you're working with.
There's a good summary of the Qt container classes (along with the std containers) in the documentation. The section on algorithmic complexity may be particularly interesting to you.
The other option is to use a vector to hold the data in key-value pairs. The vector can then be accessed by index or by key.
Disadvantage of this is that inserting into a vector is expensive relative to a std::map.
typedef std::pair<QString, State*> StateP;
typedef std::vector<StateP> States;
States states;
Then maintain the vector in sorted order based on a predicate that compares the first element. You can the lookup items by index in O(1) or key in O(log n).
struct StatePCompare {
bool operator()(StateP const& lhs, StateP const& rhs) const {
return (lhs.first < rhs.first);
}
};
void Avtomat::insert(QString key, State* state)
{
States::iterator i = std::lower_bound(states.begin(), states.end(), StatePCompare());
if ((i != states.end() && (i->first == key)) {
// key already exists, set the element
i->second = state;
}
else {
states.insert(i, state);
}
}
State* Avtomat::find(QString key)
{
States::iterator i = std::lower_bound(states.begin(), states.end(), StatePCompare());
if ((i != states.end() && (i->first == key)) {
return i->second;
}
return NULL;
}