strange iterator behavior in for loop in g++ - c++

I got this strange issue following is the code snippet which is working properly.
std::multimap<long,int>::iterator dateItr = reconnQueueDates.begin();
while( dateItr!=reconnQueueDates.end() ){
LOG_DEBUG("RUN comparision " <<cDateTime<< ", "<<dateItr->first);
if(dateItr->first <= cDateTime){
long nextTimeout = -1;
if( (nextTimeout = callReconnects(dateItr->second,dateItr->first))>-1){
if(nextTimeout>0){
reconnQueueDates.insert(std::pair<long , int>(nextTimeout, dateItr->second));
}
reconnQueueDates.erase(dateItr);
LOG_DEBUG("modified the iterator ressetting");
dateItr = reconnQueueDates.begin();
LOG_DEBUG("resset iter");
}//end of callreconnect if
}else{
++dateItr;
} //else for datetime check
}//end of while
Before this I was using a for loop with ++dateItr in the loop as follows
for( ;dateItr!=reconnQueueDates.end();++dateItr ){
LOG_DEBUG("RUN comparision " <<cDateTime<< ", "<<dateItr->first);
if(dateItr->first <= cDateTime){
long nextTimeout = -1;
if( (nextTimeout = callReconnects(dateItr->second,dateItr->first))>-1){
if(nextTimeout>0){
reconnQueueDates.insert(std::pair<long , int>(nextTimeout, dateItr->second));
}
reconnQueueDates.erase(dateItr);
LOG_DEBUG("modified the iterator ressetting");
dateItr = reconnQueueDates.begin();
LOG_DEBUG("resset iter");
}// callReconnect
} // check datetime
}// for loop
While debugging I found out that after changing the map inside the loop the iterator value inside the for construct was still using the old address.
I am using and ubuntu 12.04 with g++ version 4.6.3. Seems to me some kind of compiler bug or some kind of optimization doing this.
Any idea which flag or a bug it might be.

After reconnQueueDates.erase(dateItr); the iterator in dateItr is invalid and any use of it is undefined behaviour. Since both your old and your new for loop use it afterwards, the fact that your new version "works" is purely accidental.
The correct way to do it is to first extract all data you might still need (including the position of the next iterator) before erasing that element. For example:
std::multimap<long,int>::iterator dateItr = reconnQueueDates.begin();
while( dateItr!=reconnQueueDates.end() )
{
LOG_DEBUG("RUN comparision " <<cDateTime<< ", "<<dateItr->first);
if(dateItr->first <= cDateTime)
{
std::multimap<long,int>::iterator nextItr = dateItr;
++nextItr;
long nextTimeout = -1;
if( (nextTimeout = callReconnects(dateItr->second,dateItr->first))>-1)
{
std::pair<long , int> newentry = std::make_pair(nextTimeout, dateItr->second);
reconnQueueDates.erase(dateItr);
if(nextTimeout>0)
{
reconnQueueDates.insert(newentry);
}
LOG_DEBUG("modified the iterator resetting");
nextItr = reconnQueueDates.begin();
LOG_DEBUG("reset iter");
}//end of callreconnect if
dateItr = nextItr;
}
else
{
++dateItr;
} //else for datetime check
}//end of while

Related

list.remove_if() crashing the program

I'm working on a game and I'm trying to add collectables. I'm trying to remove the object from the list after the player has collided with it, but it ends up crashing and says:
Unhandled exception thrown: read access violation.
__that was 0xDDDDDDE9.
It says this on the for loop statement, but I think it has to do with the remove_if() function.
Here is my code:
for (sf::RectangleShape rect : world1.level1.brainFrag) {
collides = milo.sprite.getGlobalBounds().intersects(rect.getGlobalBounds());
if (collides == true) {
world1.level1.brainFrag.remove_if([rect](const sf::RectangleShape val) {
if (rect.getPosition() == val.getPosition()) {
return true;
}
else {
return false ;
}
});
brainFrag -= 1;
collides = false;
}
}
if (brainFrag == 0) {
milo.x = oldPos.x;
milo.y = oldPos.y;
brainFrag = -1;
}
I don't understand your approach, you loop the rects, then when you find the one you want to remove, you search for it again through list<T>::remove_if.
I think that you forgot about the fact that you can use iterators in addition to a range-based loop:
for (auto it = brainFrag.begin(); it != brainFrag.end(); /* do nothing */)
{
bool collides = ...;
if (collides)
it = world1.level1.brainFrag.erase(it);
else
++it;
}
This allows you to remove the elements while iterating the collection because erase will take care of returning a valid iterator to the element next to the one you removed.
Or even better you could move everything up directly:
brainFrag.remove_if([&milo] (const auto& rect) {
return milo.sprite.getGlobalBounds().intersects(rect.getGlobalBounds())
}
A side note: there's no need to use an if statement to return a boolean condition, so you don't need
if (a.getPosition() == b.getPosition()
return true;
else
return false;
You can simply
return a.getPosition() == b.getPosition();

MariaDB Connector C, mysql_stmt_fetch_column() and memory corruption

I'm working on a wrapper for MariaDB Connector C. There is a typical situation when a developer doesn't know a length of a data stored in a field. As I figured out, one of the ways to obtain a real length of the field is to pass a buffer of lengths to mysql_stmt_bind_result and then to fetch each column by calling mysql_stmt_fetch_column. But I can't understand how the function mysql_stmt_fetch_column works because I'm getting a memory corruption and app abortion.
Here is how I'm trying to reach my goal
// preparations here
...
if (!mysql_stmt_execute(stmt))
{
int columnNum = mysql_stmt_field_count(stmt);
if (columnNum > 0)
{
MYSQL_RES* metadata = mysql_stmt_result_metadata(stmt);
MYSQL_FIELD* fields = mysql_fetch_fields(metadata);
MYSQL_BIND* result = new MYSQL_BIND[columnNum];
std::memset(result, 0, sizeof (MYSQL_BIND) * columnNum);
std::vector<unsigned long> lengths;
lengths.resize(columnNum);
for (int i = 0; i < columnNum; ++i)
result[i].length = &lengths[i];
if (!mysql_stmt_bind_result(stmt, result))
{
while (true)
{
int status = mysql_stmt_fetch(stmt);
if (status == 1)
{
m_lastError = mysql_stmt_error(stmt);
isOK = false;
break;
}
else if (status == MYSQL_NO_DATA)
{
isOK = true;
break;
}
for (int i = 0; i < columnNum; ++i)
{
my_bool isNull = true;
if (lengths.at(i) > 0)
{
result[i].buffer_type = fields[i].type;
result[i].is_null = &isNull;
result[i].buffer = malloc(lengths.at(i));
result[i].buffer_length = lengths.at(i);
mysql_stmt_fetch_column(stmt, result, i, 0);
if (!isNull)
{
// here I'm trying to read a result and I'm getting a valid result only from the first column
}
}
}
}
}
}
If I put an array to the mysql_stmt_fetch_column then I'm fetching the only first field valid, all other fields are garbage. If I put a single MYSQL_BIND structure to this function, then I'm getting an abortion of the app on approximately 74th field (funny thing that it's always this field). If I use another array of MYSQL_BIND then the situation is the same as the first case.
Please help me to understand how to use it correctly! Thanks
Minimal reproducible example

Why does this function get wrong argument from iterator?

I'm writing a Space Invaders clone for IT project on uni. All the methods work, but I have a problem with deleting enemies.
I redone my code to use iterators. I store enemies in vector(horizontal) of vectors(vertical) of Enemy. The code works fine until i shoot more than 2 projectiles at a time when it gives me an error.
if (projectiles.size() != 0)
{
for (auto itr_columns = enemies.begin(); itr_columns != enemies.end(); itr_columns++)
{
for (auto itr_rows = itr_columns->begin(); itr_rows != itr_columns->end();)
{
if (projectiles.size() == 0)
{
break;
}
for (auto itr_projectiles = projectiles.begin(); itr_projectiles != projectiles.end();)
{
if (itr_projectiles->Collision(*itr_rows))
{
itr_projectiles = projectiles.erase(itr_projectiles);
itr_rows = itr_columns->erase(itr_rows);
}
else
{
itr_rows++;
itr_projectiles++;
}
}
}
}
}
That's the error i get:
line: if (itr_projectiles->Collision(*itr_rows))
Expression: can't dereference out of range vector iterator
Look at the instruction :
itr_rows = itr_columns->erase(itr_rows);
By doing that, you invalidate the iterators of itr_columns while you're are in the loop, which does use these iterators.
Never call functions invalidating iterators when you a are in a loop.

List Iterator not Dereferenceable Run time

portalManager::portalManager(SDL_Renderer* argRenderer)
{
mLootManager = new lootManager();
mLootManager->initialize();
lootPortal* newPortal;
newPortal = new lootPortal(128, 128, Portal::eForest, Tile::eForest);
mPortalList.push_back(newPortal);
newPortal = new lootPortal(256, 256, Portal::eForest, Tile::eForest);
mPortalList.push_back(newPortal);
mPortalSheet = new spriteSheet(192, 192, 0);
mPortalSheet->loadTexture("Images/Portals.png", argRenderer);
mRenderQuad.w = Tile::cTileSize;
mRenderQuad.h = Tile::cTileSize;
mTextureQuad.w = Tile::cTileSize;
mTextureQuad.h = Tile::cTileSize;
}
void portalManager::render(int argX, int argY, int argW, int argH, SDL_Renderer* argRenderer)
{
std::list<lootPortal*>::const_iterator itr = mPortalList.begin();
for (itr = mPortalList.begin(); itr != mPortalList.end(); itr++);
{
std::cout<<(*itr)->getX()<<std::endl;
mRenderQuad.x = (*itr)->getX();
mRenderQuad.y = (*itr)->getY();
if ((mRenderQuad.x >= argX && mRenderQuad.x <= argX+argW) && (mRenderQuad.y >= argY && mRenderQuad.y <= argY + argH))
{
mTextureQuad.x = 0;
mTextureQuad.y = 0;
}
}
};
The problem happens in the for loop of render when I try to dereference the iterator.
I have checked that the list is not empty I can access the first and last element of the list but for some reason the for loop always throws a list Iterator not dereferenceable.
The problem is in this line:
for (itr = mPortalList.begin(); itr != mPortalList.end(); itr++);
^
You have a spurious semi-colon at the end of the for clause. Easy fix - get rid of it.
If you're curious as to why this causes a problem - what this means is that the entire loop will complete before it proceeds to execute the code in the body of the loop (because that code isn't actually in the body of the loop after all). And once it gets to that code, itr equals mPortalList.end().

Segmentation fault bintree

I'm trying to implement the bintree, but I have problems in the insert method.
If I add the first element, the program dont crash but, when I introduce 2 or more element the program crash.
This is the code
template <typename T>
void Arbol<T>:: insertar( T c){
if(laraiz==0)
{
laraiz=new celdaArbol;
laraiz->elemento=c;
laraiz->padre=laraiz->hizqu=laraiz->hder=0;
}
else {
celdaArbol *com=laraiz;
bool poner=false;
while(poner==false){
if(c>com->elemento){
if(com->hder==0){
com->hder= new celdaArbol;
com->hder->elemento=c;
com->hder->padre=com;
poner=true;
}
else{
com=com->hder;
}
}
else {
if(com->hizqu==0){
com->hizqu= new celdaArbol;
com->hizqu->elemento=c;
com->hizqu->padre=com;
poner=true;
}
else {
com=com->hizqu;
}
}
}
}
}
I think that the problem is in the else:
else{
com=com->hizqu; //com=com->hder;
}
Because I saw in the debugger that the program enter several times in the section and should not do.
According to this code:
laraiz->padre=laraiz->hizqu=laraiz->hder=0;
You do not properly intialize pointers hizqu and hder to nullptr in constructor of celdaArbol class. And you do not initialize them in either branch of if(c>com->elemento){ so they seem to have garbage values.
Also your code can become more readable and less error prone if you use proper C++ constructions:
celdaArbol *com=laraiz;
while( true ){
celdaArbol *&ptr = c > com->elemento ? com->hder : com->hizqu;
if( ptr ) {
com = ptr;
continue;
}
ptr = new celdaArbol;
ptr->elemento=c;
ptr->padre=com;
ptr->hder = ptr->hizqu = nullptr;
break;
}
This code is logically equal to yours, except it shorter, easier to read, avoid duplication and fixes your bug.
For every leaf node (except the root of the tree), you never initialize the left child or right child node to be anything but an unspecified value.
You probably meant to initialize them as nullptr.
Here's one example:
if (com->hizqu==0){
com->hizqu = new celdaArbol;
com->hizqu->elemento = c;
com->hizqu->padre = com;
poner = true;
// What is the value of com->hizqu->hizqu?
// What is the value of com->hizqu->hder?
}