vector iterators incompatible C++ - c++

Okay, looked for it over the web and inside stackoverflow for about an hour and did not find anything like this, sure there are several cases, but none of them seems to have the same root.
Okay, I need to iterate over a multimap and they are stored in a vector. Here goes the code:
DataVector::iterator curr = m_rawBuffer[0]->begin();
DataVector::iterator last = m_rawBuffer[0]->end();
for( ; curr != last; ++curr) {
delete (*curr);
}
delete m_rawBuffer[0];
DataVector::iterator curr2 = m_rawBuffer[1]->begin();
DataVector::iterator last2 = m_rawBuffer[1]->end();
for( ; curr2 != last2; ++curr2) {
delete (*curr2);
}
delete m_rawBuffer[1];
DataVector is a std::multimap<MyData*>.
m_rawBuffer is a std::vector<DataVector*>.
The strangest thing is that the first iterator on m_rawBuffer[0] works fine, but iterating over m_rawBuffer[1] does not work! And to get even more strange it breaks only in one machine, it runs over distributed system and all other machines are fine.
The operator[] from std::vector returns a reference, right?

Related

deleting a bunch of llvm instructions

I was wondering how we can delete a bunch of instructions in LLVM.
I tried the following ( based on a post from llvm-dev mailing list )
// delete all instructions between [start,end)
void deleteAllInstructionsInRange(Instruction* startInst,Instruction* endInst)
{
BasicBlock::iterator it(startInst);
BasicBlock::iterator it_end(endInst);
it_end--;
Instruction* currentInst ;
while(it != it_end )
{
currentInst = &*it;
// this cannot be done at the end of the while loop.
// has to be incremented before "erasing" the instruction
++it;
if (!currentInst->use_empty())
{
currentInst->replaceAllUsesWith(UndefValue::get(currentInst->getType()));
}
currentInst->eraseFromParent();
}
}
Everything works as expected except for the last iteration.
Anyone understand why ? ( I've tried using gdb but it gives a segfault error in
the last iteration)
The way your loop is built makes the inner code try to delete what is an invalid iterator: it == it_end. A simple if (it == it_end) continue; after it++ will help.
Not sure how LLVM iterables are built, but for the stl containers, erase will return an incremented iterator, so you won`t even need the weird loop. A brief look at the docs seem to confirm this.

Rehashing a hash table comprised of a vector of lists C++

So I have implemented a hash table storing pointers. I implemented it as a vector of lists. Along the lines of std::vector<std::list<Sequence*> > hashTable;. I have implemented everything and it works fine. My only problem arises when I try to rehash the hash table. The hash table uses separate chaining to resolve collisions, hence my vector of lists.
Using the debugger, I have found that my loop to rehash is hanging on the while loop and causing an infinite loop. It constantly reads from the first index of my old vector. I am new to this and I am not sure of the proper way to rehash this table. I tried to research and found not many references to this implementation of a hash table, much less rehashing one. Here is my implementation.
void HashTable::rehash(int oldSize){
int tempSize = 2 * oldSize;
int newSize = HashTable::findPrime(tempSize);
std::vector<std::list< Sequence*> > newHashTable;
newHashTable.resize(newSize);
tableSize = newSize;
for(int i = 0; i < oldSize; i++){
auto iter = hashTable[i].begin();
while(iter != hashTable[i].end()){
if((*iter) != nullptr){
std::string tempString = (*iter)->getKey();
int hashIndex = hashFunction(tempString);
newHashTable[hashIndex].push_front(*iter);
}
}
}
hashTable = newHashTable;
}
I feel like it is a simple error, I am just not seeing it. Or am I completely going about this the wrong way?
You don't seem to update iter, that's why iter will never become hashtable[i].end() unless it starts with that value.
You are missing a ++iter before the end of your while loop so it moves on to the next on the list.
I would normally use a for loop myself as it makes me less likely to forget to increment the iterator. Something like:
for(auto iter = hashTable[i].begin(); iter != hashTable[i].end(): ++iter)
{
// ... code using iterator ...
}

Map and iterator (in)validation

I have a cache of items stored in a map that needs to be cleaned on occasion. map::erase invalidates the curr iterator, so I want to maintain a next pointer in case curr is deleted.
Question: if I erase the element at curr, does that invalidate next? I don't believe it does, but I want to be sure I'm not missing something obvious from map::erase).
The code below also fails to compile with error: no match for ‘operator+’ in ‘curr + 1’:
typedef map<string, SSL_CTX_ptr> Contexts;
...
Contexts::const_iterator curr, next;
curr = contexts.begin();
while (curr != contexts.end())
{
if(IsOkToRemove(curr))
{
next = curr + 1;
contexts.erase(curr);
curr = next;
continue;
}
curr++;
}
Question: how do I set next to the element following curr?
EDIT: Kerrek's example below is the exact situation I was trying to understand:
contexts.erase(it++);
Within erase, the element is erased. That means the caller's iterator to the same element is invalidated before the post increment occurs. Because the iterator is invalidated, its the reason I'm trying to do curr + 1 above. And its the reason I wanted to ensure next was always valid.
Like this:
for (auto it = contexts.begin(); it != contexts.end(); )
{
if (IsOkToRemove(it)) { contexts.erase(it++); }
else { ++it; }
}
Since C++11 you can also say it = contexts.erase(it);, since erase was changed to return the iterator past the erased element in C++11.

deallocating memory in a map with pointers

I was trying to erase pointer elements (the value in the map is a pointer) from the map and I saw the code here What happens to an STL iterator after erasing it in VS, UNIX/Linux?
for(map<T, S*>::iterator it = T2pS.begin(); it != T2pS.end(); T2pS.erase(it++)) {
// wilhelmtell in the comments is right: no need to check for NULL.
// delete of a NULL pointer is a no-op.
if(it->second != NULL) {
delete it->second;
it->second = NULL;
}
}
I am not sure if the 'delete it->second' with de-allocate the correct memory because the erase(it++) step already moves the iterator to the next object. By the time, it reaches the delete statement, it is pointing to the next element which we don't want to delete. Am I missing something?
I believe this will work as expected.
The third section of the for loop (where the iterator is erased and then incremented) executes after the first iteration, and so on for each relevant iteration. Thus, you're always erasing the element you've already "dealt with" in the loop contents.
A parallel example:
for (int i = 0; i < 1; ++i) { ...
You will still enter the loop and execute with i = 0 before incrementing i and checking the looping condition.
You may want to try another way:
while (T2pS.size() > 0) {
if (T2pS.begin()->second != NULL) {
delete T2pS.begin()->second;
}
T2pS.erase(T2pS.begin());
}

Accessing list element pointed by an iterator

The natural answer would be to dereference the iterator and get the value. However, I'm stuck at using VC++ 2010 which doesn't allow dereferencing the list iterator (or does it?)
I'm confused because, at one point, I need to dereference two list iterators and compare their values using:
(*it) == (*it2)
The program crashes with an error, only due to this line. I'm also dereferencing the iterator in a statement:
printf("%d\n", (*it));
This works perfectly fine though.
So, is there any way to access an element without dereferencing or using a cliext::list.
for (it=sList.begin(); it != sList.end(); it++)
{
for (it2=it; it2 != sList.end(); it2++)
{
it2++;
if ((*it) == (*it2))
{
sList.erase(it, it2);
}
it2--;
}
}
The error I get is:
Debug Assertion Failed
Expression: list iterator not dereferencable
Surprisingly the same code runs without a problem when compiled on DevC++ (MinGW)
You can in fact dereference list iterators. If you couldn't, your comparison code wouldn't have even compiled. Most likely you're accidentally dereferencing an end iterator though rather than a valid one, causing the crash. Without more code it's hard to make further observations.
EDIT: I can't make out quite what it is you're trying to do. The code you've written erases all the elements between two equal elements. I'll assume you're actually trying to remove all the duplicate elements, and that sorting the list first for performance isn't a concern/option.
EDIT2: I saw in a comment below you really want to delete the range. Updated my code.
Then try something like this:
for (it=sList.begin(); it != sList.end(); ++it)
{
it2 = it;
++it2;
while(it2 != sList.end())
{
if ((*it) == (*it2))
{
it = it2 = sList.erase(it, it2); // Reset it as well since it will be blown away. It'll still point to the same value it did before though.
}
else
++it2;
}
}
Its surely your code. It has two problems as far as I can see. Checkout the comments.
for (it2=it; it2 != sList.end(); it2++)
{
it2++;
// there is no guarantee that it2 will now be valid
// it should be validated again
if ((*it) == (*it2))
{
// you should not modify the list here.
// this will invalidate your iterators by default.
sList.erase(it, it2);
}
it2--;
}
Try this instead:
for (it=sList.begin(); it != sList.end(); it++)
{
for (it2=sList.end()-1; it2 != it+1; it2--)
{
if ((*it) == (*it2))
{
it = sList.erase(it, it2)-1;
break;
}
}
}
This new version avoids two errors in the original version of the code. First, the code now properly handles the edge conditions of the inner for loop. In the original code, the for loop allowed it2 to go up to sList.end()-1, but then the next line incremented it to sList.end() on the last iteration. The next line then dereferenced this (invalid) iterator which is one past the last value of the list (because that's what end returns, it's not an iterator to the last value of the list).
Second, calling erase invalidates any iterators pointing to any of the values erased (which in this case would including any iterators from it to it2-1). By starting at the end of the list and working our way forward, we no longer have to continue iterating when we find the value, and can break from the inner loop once we find it. erase returns an iterator to the next element in the list after the elements deleted (which would be the next element we want to try for it). But since the for loop increments it, we subtract 1 from what's returned by erase so that it points to the right element once it's incremented at the beginning of the next loop iteration. (Note that in the case that it points to the first element, we actually temporarily set it to point an element before the beginning of the list; however, this is only temporary and we don't dereference the iterator while it's pointing outside the list).
Note that this preserves the original behavior of the code for the case 0 2 3 4 5 1 6 7 8 0 9 10 11 1. You haven't explicitly stated what order the deletes should occur (should the elements between 0's be erased first, or the elements between 1's, or do we need to add additional logic to actually erase the whole range except for the first 0 and 1?), but this code behaves like the original and erases the numbers in between the 0's and ignores the fact that the 9 10 11 afterwards was original in between matching 1's.
"select" Isn’t Broken.
It is rare to find a bug in the OS or
the compiler, or even a third-party
product or library. The bug is most
likely in the application. - from The
Pragmatic Programmer
It's highly likely due to your problem, not MS. Make it sure that your iterators are not invalidated while you are using them. You could accidentally erase the element which invalidate the iterator. Check this thread: What is the lifetime and validity of C++ iterators?
and Good Luck! :)
UPDATE:
As I mentioned earlier, you are invalidating your iterators by erasing them in the middle of the loop. See my code below to do it properly.
std::list<int>::iterator EraseElements(std::list<int>& sList, std::list<int>::iterator start)
{
for (std::list<int>::iterator itor1 = start; itor1 != sList.end(); ++itor1)
{
std::list<int>::iterator itor2(itor1);
++itor2;
for ( ; itor2 != sList.end(); ++itor2)
{
if ((*itor1) == (*itor2))
{
return sList.erase(itor1, itor2);
}
}
}
return sList.end();
}
void main()
{
// Test
list<int> sList;
sList.push_back(1);
// elements will be erased
sList.push_back(2);
sList.push_back(3);
//
sList.push_back(2);
sList.push_back(4);
sList.push_back(5);
// elements will be erased
sList.push_back(6);
sList.push_back(7);
//
sList.push_back(6);
list<int>::iterator next = sList.begin();
while (next != sList.end())
{
next = EraseElements(sList, next);
}
// It will print 1 2 4 5 6
for (std::list<int>::const_iterator itor = sList.begin(); itor != sList.end(); ++itor)
{
cout << *itor << endl;
}
}
It is really unclear what this code snippet or whatever code you get the error from is trying to do.
It appears what you want to do is for each item delete all items between it and the next matching item, or maybe it is the last matching item.
your inner loop iteration is double stepping from the loop increment and then incrementing again inside the loop.
your not checking if you have hit/passed the end of the list after doing the inner iteration which could lead to the crash when doing the comparison
after erasing you decrement it2, which then puts it before what it1 was (and is now deleted).