Overriding QAbstractItemModel::index and accessing std::map - c++

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;
}

Related

Accessing Each Node in a list that is an index in a vector

I am currently making a hash table by using a private data member vector<list<pair<K, V>>> hashTable;.
I need to access each list, and in turn, each pair for various different functions. I am currently doing so like:
for(int i = 0; i < hashTable.capacity(); i++){
list<pair<K,V>>* listPtr = hashTable[i];
for(pair<K,V>* pairPtr = listPtr->front(); pairPtr != listPtr->end(); pairPtr++){
pair<K,V> tempPair;
tempPair.first = pairPtr->first;
tempPair.second = pairPtr->second;
insert(tempPair);
}
}
}
The code above is part of my rehash function. Insert inserts the pair based on the hash functions which hashes based on the size of the vector. It's not really important. I just want to know how to get to each list and then each pair.
My question is, is there a better way to access each list and pair in the vector?
The simple way to access each pair on each list is
for (const auto& list : hashTable)
{
for (const auto& pair : list)
{
...
}
}
but the insert function in the code above worries me. If you are iterating over the vectors/lists while you are simultaneously modifying them, that changes things.

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

Multi-index on boost::ptr_vector

I have the following classes in a program.
class Class1 {
public:
boost::ptr_vector<Class2> fields;
}
class Class2 {
public:
std:string name;
unsigned int value;
}
I want to write a member function in Class1 that returns a reference or pointer to an element in fields based on Class2's name variable. I don't have to be concerned with the lifetime of the objects in the container.
Currently, I am returning an iterator to the element I want after the function searches from the start of the vector to the element.
boost::ptr_vector<Class2>::iterator getFieldByName(std::string name) {
boost::ptr_vector<Class2>::iterator field = fields.begin();
while (field != fields.end()) {
if (field->name.compare(name) == 0) {
return field;
}
++field;
}
return fields.end();
}
The problems that I'm facing are:
(1.) I need to have fast random access to the elements or the program sits in getFieldByName() too long (a boost::ptr_vector<> is too slow when starting at the beginning of the container)
(2.) I need to preserve the order of insertion of the fields (so I can't use a boost::ptr_map<> directly)
I have discovered Boost::MultiIndex and it seems like it could provide a solution to the problems, but I need to use a smart container so that destruction of the container will also destruct the objects owned by the container.
Is there anyway to achieve a smart container that has multiple methods of access?
You can use two containers. Have a boost::ptr_map<> that stores the actual data, and then have a std::vector<> that stores pointers to the nodes of the map.
boost::ptr_map<std::string, Class2> by_field;
std::vector<Class2 const*> by_order;
void insert(Class2* obj) {
if (by_field.insert(obj->name, obj).second) {
// on insertion success, also add to by_order
by_order.push_back(obj);
}
}
This will give you O(lg n) access in your getFieldByName() function (just look it up in by_field) while also preserving the order of insertion (just look it up in by_order).

How to implement an API for a distributed map in c++?

I am implementing a distributed map in c++ and searching for a good API design.
First and straightforward option is to make it exactly like std::map. Problem is with iterator.
IMap<std::string,Person>::iterator it;
it = map.find("sample");
if(it == map.end() ){
//NULL
}
for(it = map.begin(); it != map.end(); it++){
//iterate
}
In distributed context(at least in the one i am implementing) , there is no begin and end of the map. It is not ordered in anyway, so returning an iterator does not look like an option.
Second option is returning the value class by copy like below:
Person emptyPerson;
Person person = map.get("sample");
if(person == emptyPerson){
//NULL
}
Problem is with that NULL check looks strange. You can first ask if it is available and then get the object, but the requirement is that these operations must be atomic.
Third option is returning pointer:
Person* person = map.get("sample");
if(person == NULL){
//NULL
}
I don't want to do it this way, because it is error prone. User needs to delete the pointer that i created internally.
I am thinking about returning a class that wrapping user object like:
value_reference<std::map, Person> person = map.get("sample");
if(value_reference.hasValue() ){
Person p = value_reference;
}
So what do you think the best approach is?
Do you know any good api similar to requirements my distributed map?
Based on your term "distributed map" I am making the following assumptions:
A subset of the data is available locally, and for the set of data that is not some remote-fetch will need to be performed.
Writes to the returned object should not be automatically persisted in the data store. An explicit update request should be made instead.
If this is true then iterators are not what you want, nor do you want the STL container model. The C++ Iterator concept requires you to implement the pre-increment (++i) operator, and if your data is unordered and spread across multiple nodes, then the request "give me the next entry" does not make sense.
You could create a terrible kludge if you wanted to simulate STL containers and iterators for interoperability reasons: have the map's end() method return a sentinel iterator instance, and have operator++() for your iterators return this same sentinel. Effectively, every iterator would point to "the last element in the map." I would strongly advise against taking this approach unless it becomes necessary, and I don't think it will be.
It sounds like what you want is a simple CRUD model, where updates must be explicitly requested. In that case, your API would look something like:
template <typename TKey, typename TValue>
class IMap<TKey, TValue>
{
public:
void create(TKey const & key, TValue const & value) = 0;
std::unique_ptr<TValue> retrieve(TKey const & key) = 0;
bool update(TKey const & key, TValue const & value) = 0;
bool remove(TKey const & key) = 0;
};
In the retrieve case, you would simply return a null pointer as you suggested. std::unique_ptr<> will ensure that the caller will either delete the allocated object or explicitly take ownership of it.
An alternative to the "return pointer to newly-allocated object" case would be to let the caller pass in a reference, and the method would return true if the value was found in the map. This will, for example, let the caller retrieve an object directly into an array slot or other local structure without the need for an intermediary heap allocation.
bool retrieve(TKey const & key, TValue & value) = 0;
Use of this method would look something like:
Person person;
if (map.retrieve("sample", person)) {
std::cout << "Found person: " << person << std::endl;
} else {
std::cout << "Did not find person." << std::endl;
}
You could provide both overloads too, and the one returning a pointer can be implemented in terms of the other by default:
template <typename TKey, typename TValue>
std::unique_ptr<TValue> IMap<TKey, TValue>::retrieve(TKey const & key)
{
TValue v;
return std::unique_ptr<TValue>(retrieve(key, v) ? new TValue(v) : nullptr);
}
I'd say something like option 3 is best. You could just emulate it using one of the standard smart pointer types introduced in C++11, so you still create a pointer, but the user doesn't have to free it. So something like:
std::unqiue_ptr<Person> person = map.get("sample");
if(person) {
person->makeMeASandwitch();
}

Change the value in std::map from iterator

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.