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.
Related
I have:
std::unordered_set<ObjectRepresentation*> incompletePieces;
I would like to get exactly one object from the unordered_set. To do that I am using a for loop, and "break", at the end of the loop so that the loop runs at most once.
while (incompletePieces.size()){
for (auto containedPiece : incompletePieces){ //Warning at this line that loop will run at most once
// .... doing some stuff with the contained piece
incompletePieces.erase(containedPiece);
break;
}
}
This is the desired behaviour that I want. The problem is that the compiler shows a warning:
Loop will run at most once (loop increment never executed)
How do I rewrite my code so that the warning goes away ? Is there a better way to get an item from the unordered_set ?
You could use begin() to get the first element.
if (incompletePieces.size() > 0)
auto containedPiece = *(incompletePieces.begin());
The code you presented does in fact process all elements and clears the set of them as it gets done, but it does so in a highly unidiomatic way.
There are two idiomatic ways of doing this, depending on whether processing an element could modify the set itself.
1) If the "doing some stuff" code is guaranteed to not touch incompletePieces (i.e. completing one piece does not create additional incomplete pieces), then the idiomatic and efficient solution is to just loop over the set and clear it afterwards:
for (auto piece : incompletePieces) {
// process piece
}
incompletePieces.clear();
2) If this is not the case, or you really need to clear elements as you go, then the idiomatic solution is still iterator based looping:
auto it = incompletePieces.begin();
while (it != incompletePieces.end()) {
// process *it
#if C++11
it = incompletePieces.erase(it);
#else
auto prev = it++;
incompletePieces.erase(prev);
#endif
}
Whereas *unordered_set::begin() will give you first element (no unordered_set::front()),
I would rewrite:
while (incompletePieces.size()){
for (auto containedPiece : incompletePieces){
// .... doing some stuff with the contained piece
incompletePieces.erase(containedPiece);
break;
}
}
into:
for (auto* containedPiece : incompletePieces){
// .... doing some stuff with the contained piece
}
incompletePieces.clear();
You can rewrite the code as below:
for(auto* containedPiece : incompletePieces){
//Process the set contents
}
//Clear entire set in one go
incompletePieces.clear();
If you want to clear it one by one, you would have to use iterators as shown below:
auto it = incompletePieces.begin(); //Take the pointer to first element of set
for( ; it !=incompletePieces.end() ; it++){
incompletePieces.erase(*it); //Erase one element at a time
}
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?
Is it correctly possible to walk sequently through QMap with help of iterators, and doing such actions: removing some items and adding new ones?
For example:
for( QMap<key_t,val_t>::iterator it = map.begin();
it != map.end();
++it )
{
if( it->value == something )
{
map.erase(it);
map.insert(it->key+10,it->value);
}
}
It seems, that nothing will be done wrong, I'm asking to be sure. (I have no enough time to check it).
UPD Will solve with QMap::unite():
for( QMap<key_t,val_t>::iterator it = map.begin();
it != map.end();
++it )
{
if( it->value == something )
{
tmp_map.insert(it->key+10,it->value);
map.erase(it);
}
}
map.unite(tmp_map);
Thanks for answers!
The iterator will be invalidated by erase, and so cannot safely be used or incremented afterwards. The following should work:
for( QMap<key_t,val_t>::iterator it = map.begin(); it != map.end(); )
{
if( it->value == something )
{
map.insert(it.key()+10,it.value());
it = map.erase(it);
} else {
++it;
}
}
Think about it a little while... You are iterating over a collection, removing an item in the middle and adding another item somewhere else. Will the iterators still be correct? Will the "next" iterator really be the next item?
In general it is not a good idea to change a collection you are iterating over. If you need to then use a temporary collection and copy selected items over to that, and clear the real collection and move the items from the temporary collection over to the real one.
In your case though, why not use QMap::find to search for something, and if found erase it and add the new item, and do it in a loop until something is not found anymore?
I would expect it to be invalid after map.erase(it), in which case it->value and ++it will not work.
You have to 'reset' your iterator to the one returned by erase and insert. It's fine in principle though.
The following code snippet gives the error message at run time of:
Debug assertion failed: Vector iterator not dereferenceable
for(it=stat1vec.begin(); *(it)>=investigated_stat; it++, positioner++)
{
if(*it==investigated_stat)
equalwith++;
}
When changed to:
for(it=stat1vec.begin(); *(it)==investigated_stat; it++, positioner++)
{
if(*it==investigated_stat)
equalwith++;
}
The code works perfectly.
All the has been done is to change >= to ==
Why is this?
Thanks very much
You should test for the iterator to be a valid iterator before dereferencing it. Your loop will continue until *it < investigated_stat but it won't stop when none of the elements are >= investigated_stat and you iterate past the end of the vector.
The test in the for loop should be
it != stat1vec.end() && *it >= investigated_stat
Which makes the slightly modified for:
for(it = stat1vec.begin(); it != stat1vec.end() && *it >= investigated_stat; ++it, ++positioner)
if(*it == investigated_stat)
++equalwith;
It seems at some point before reaching the end of the vector you assigned zero to *(it): at this point the loop terminated. The logical comparison tried to find something different - and failed. I assume you meant to write == in the second loop as well.
Note that the idiomatic way to iterate over a sequence (when not using algorithms, that is) looks something like this:
for (it = v.begin(), end = v.end(); it != end /* possible additional conditions */; ++it)
{
...
}
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).