I'm a bit rusty on c++ and am returning to it to get better. I searched a bit on StackOverflow already to no avail. The issue I am having is "list iterator not incrementable". Below is the section I believe to be the culprit, as this is the only location that I use an iterator with a modifier.
if (!io_queue.empty()) {
for (list<Process>::iterator it = io_queue.begin(); it != io_queue.end(); ++it) {
if (it->isBurstDone()) {
if (it->isComplete()) {
it->setExit(clock);
complete.push_back(*it);
it = io_queue.erase(it);
}
else {
ready_queue.push_back(*it);
it = io_queue.erase(it);
}
}
else {
it->decBurst();
}
}
}
You are incrementing it unconditionally on every loop iteration. But when erase() is called, it returns a new iterator to the next list element after the one being erased, and so if that results in it being set to the end iterator than incrementing it again is undefined behavior.
Simply move the ++it from the for statement to the else block inside the loop.
Try this:
if (!io_queue.empty()) {
list<Process>::iterator it = io_queue.begin();
do {
if (it->isBurstDone()) {
if (it->isComplete()) {
it->setExit(clock);
complete.push_back(*it);
}
else {
ready_queue.push_back(*it);
}
it = io_queue.erase(it);
}
else {
it->decBurst();
++it; // <— moved here
}
}
while (it != io_queue.end());
}
Related
Hi I am a beginner in c++ and I would like to know why this code returns a Debug Assertion Failed error everytime an object is erased from the list.
for (auto it = ProjectileList.end(); it != ProjectileList.begin();) {
--it;
if (it->position_y < 0) {
ProjectileList.erase(it);
}
else {
it->Draw(window.renderer);
it->position_y--;
}
}
You have to assign the new iterator that the function erase() returns to it.
for (auto it = ProjectileList.end(); it != ProjectileList.begin();) {
--it;
if (it->position_y < 0) {
it = ProjectileList.erase(it); // assign the new iterator
}
else {
it->Draw(window.renderer);
it->position_y--;
}
}
Another solution would have been to write two loops, one that erases, and the other that draws. Using the std::remove_if could be used:
#include <algorithm>
//...
// Erase
auto iter = std::remove_if(ProjectileList.begin(), ProjectileList.end(),
[&] (auto& p) { return p.position_y < 0; });
ProjectileList.erase(iter, ProjectileList.end());
// Now draw the remaining ones.
for (auto& p : ProjectileList)
{
p.Draw(window.renderer);
p.position_y--;
}
I have the following code:
bool resetTypeBit = true;
for (auto it = eventsList.begin(); it != eventsList.end(); ++it) {
CreatureEvent* curEvent = *it;
if (curEvent == event) {
it = eventsList.erase(it);
} else if (curEvent->getEventType() == type) {
resetTypeBit = false;
}
}
So I have the following scenario: eventList contains 01 item, and then, as soon as the for statement goes through for the first time and meet the it = eventsList.erase(it); line, the it variable becomes invalid, causing a segmentation fault on the next iteration of the for statement.
Any clues of what could be causing the problem?
If the item you remove is the last item in the list, the erase method will return end(). Your for loop will then try to increment that iterator, which results in undefined behaviour.
Another problem which you haven't come across yet is that, if the item you remove isn't the last item in the list, you'll end up skipping over the following item (because the iterator is incremented past the one that erase returns). You can think of erase as an increment operation that just happens to erase the item first.
The solution is to refactor the loop slightly, to move the increment to the end (and only if erase wasn't called):
bool resetTypeBit = true;
for (auto it = eventsList.begin(); it != eventsList.end(); ) {
CreatureEvent* curEvent = *it;
if (curEvent == event) {
it = eventsList.erase(it);
}
else {
if (curEvent->getEventType() == type) {
resetTypeBit = false;
}
++it; // move the increment to here
}
}
As it is written now, you are incrementing the iterator even in the erase branch, which means that you are always skipping the element just after an erased one. This is both incorrect and results in serious problems if the last element happens to be one to delete. To fix, you have to not increment if you already fix it by setting it to the element following the deleted one.
bool resetTypeBit = true;
for (auto it = eventsList.begin(); it != eventsList.end(); ) {
CreatureEvent* curEvent = *it;
if (curEvent == event) {
it = eventsList.erase(it);
continue;
} else if (curEvent->getEventType() == type) {
resetTypeBit = false;
}
++it;
}
I am having an issue and I think it is because of the iterators being invalidated. However I use the iterator from erase() to resume iterating other the structure. When erase() when I try to increment after erase() is called the first time I get the following error
'vector iterator not incrementable '
std::map<uint32_t, std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>>::iterator itMap;
std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>::iterator itVector;
{
tvstd::lock_guard_mutex l(m_ConnectionsMutex);
itMap = m_Connections.find(static_cast<uint32_t>(pcp->ProcessID()));
if (itMap != m_Connections.end())
{
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); ++itVector)
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
}
}
}
}
Can anyone see where I am going wrong?
erase returns an iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.
so if you erase you do not need to increment your iterator
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); )
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
}
else
{
++itVector
}
}
This solved my issue, I just have to call break after i erase but once i erase i do not need to loop to the end of the list. (#Aleexander solution also works)
std::map<uint32_t, std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>>::iterator itMap;
std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>::iterator itVector;
{
tvstd::lock_guard_mutex l(m_ConnectionsMutex);
itMap = m_Connections.find(static_cast<uint32_t>(pcp->ProcessID()));
if (itMap != m_Connections.end())
{
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); ++itVector)
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
break;
}
}
}
}
I have the following code:
//update it in the map
std::map<std::string, std::string>::iterator it;
for(it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end(); ++it)
{
if(it->first == change.first)
{
if(change.second == "")
{
spreadsheets.at(i).cells.erase(change.first);
}
else
{
it->second = change.second;
}
}
}
The code above runs perfectly on my mac however when I run in on a linux computer it throws a seg fault on spreadsheets.at(i).cells.erase(change.first);
Any idea whats causing this error? Ive tried changing erase(change.first) to erase(it) and I am still getting the seg fault.
Because when you erase from the container, your iterator is no longer valid, yet your loop continues.
You could change your loop to:
std::map<std::string, std::string>::iterator it = spreadsheets.at(i).cells.begin();
while (it != spreadsheets.at(i).cells.end())
{
if(it->first == change.first)
{
if(change.second == "")
{
spreadsheets.at(i).cells.erase(it++); //Post increment returns a copy pointing at the current element, while it already points to the next element and thus stays valid after erase
}
else
{
it->second = change.second;
++it;
}
}
else
++it;
}
Now that I think of it, why do you erase with first element of the pair the iterator is pointing to, ie:
spreadsheets.at(i).cells.erase(change.first);
instead of
spreadsheets.at(i).cells.erase(it);
It's less efficient, as another lookup has to be made.
From the documentation of std::map::erase:
References and iterators to the erased elements are invalidated. Other references and iterators are not affected.
Still your loop goes on and you increment your (now invalid) iterator.
Fix: increment your iterator another way, eg.:
std::map<std::string, std::string>::iterator it;
for (it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end();/*NOTE: no increment here*/)
{
if (it->first == change.first)
{
if (change.second == "")
{
it = spreadsheets.at(i).cells.erase(it); // C++11 only
// in both C++03 and C++11: spreadsheets.at(i).cells.erase(it++);
}
else
{
it->second = change.second;
++it;
}
}
else
++it;
}
Or to avoid confusion because of the numerous execution paths (the very same confusion that made me forget the last else on my first try): just copy the iterator, increment the original one, and then use the copy. This may look overkill in your case but for more complex loops this is sometimes the only way to stay sane. ;)
std::map<std::string, std::string>::iterator it;
for (it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end();/*NOTE: no increment here*/)
{
std::map<std::string, std::string>::iterator this_it = it++;
if (this_it->first == change.first)
{
if (change.second == "")
{
spreadsheets.at(i).cells.erase(this_it);
}
else
{
this_it->second = change.second;
}
}
}
After element being erased from map pointing to this element will become invalidated. Thus spreadsheets.at(i).cells.erase(change.first); renders it invalid. See Iterator Invalidation Rules
References and iterators to the erased elements are invalidated.
//update it in the map
std::map<std::string, std::string>::iterator it;
for(it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end(); ++it)
{
if(it->first == change.first)
{
if(change.second == "")
{
spreadsheets.at(i).cells.erase(it--);
}
else
{
it->second = change.second;
}
}
}
At the moment you do spreadsheets.at(i).cells.erase(change.first); the iterator in the std::map (at the current change.first key) is invalidated. So, when you do it++, it is undefined behaviour.
cf Rules for Iterator Invalidation for rules about invalidation of iterators in standard containers
Increment the iterator before you erase it. Why didn't it happen on the Mac? Who knows.. different OS, different behaviour.
SO
I have the following function.
void BulletFactory::update(Uint32 ticks) {
std::list<Sprite*>::iterator it = activeBullets.begin();
while (it != activeBullets.end()) {
(*it)->update(ticks);
if(!((*it)->inView())) {
activeBullets.remove(*it);
Sprite* temp = *it;
it++;
inactiveBullets.push_back(temp);
} else {
it++;
}
}
}
when the condition !((*it)->inView()) is being true there is a segmentation fault. I am not able to see the problem.
edit: forgot to mention that activeBullets and inactiveBullets are two lists.
activeBullets.remove(*it);
Sprite* temp = *it; //<--- problem
it++; //<-- problem
should be:
Sprite* temp = *it;
it = activeBullets.erase(it); //use erase
//it++; don't increment it
You must not modify the element that the iterator is pointing to, because it invalidates the iterator. See this question for solutions.