segmentation fault in iterator - c++

I have created a multimap for my road points. The key refers to the road number and the values are vec3 points that make up the road.
I am trying to iterate through the values of each key point and create a road segment at each point on the road (except the last), adjust the values to be on the road points and then store them in a std::vector.
The RoadSegment constructor creates 6 vec3 points and pushes them onto a std::vector.
I have a segmentation fault in the line marked in bold
[for(mapIt = it.first; mapIt != it.second; ++mapIt)]
When i take out the lines creating the new objects and pushing them onto the std::vector it works fine.
Can anyone tell me what the problem is / a solution to the problem??
Many thanks in advance
std::vector<glm::vec3>::iterator SegIt;
for(int i = 0; i < m_genRoads->getKeyValueData().size(); i++)
{
int numberDesired = m_genRoads->getMultimapData().count(i) - 1;
std::multimap<int, glm::vec3>::iterator mapIt;
std::pair<std::multimap<int, glm::vec3>::iterator, std::multimap<int, glm::vec3>::iterator> it;
it = m_genRoads->getMultimapData().equal_range(i);
for(mapIt = it.first; mapIt != it.second; ++mapIt)
{
int distance = std::distance(it.first, mapIt);
if(distance != numberDesired)
{
RoadSegement* roadSegmentPointer = new RoadSegement();
// FUNCTIONS TO ADJUST COORD VALUES TO MATCH THE ROAD POINTS
m_segmentArray.push_back(roadSegmentPointer);
}
else
{
continue;
}
///SOME BUFFER BINDING STUFF

The issue seems to be that you're using iterators that do not exist, all due to returning a temporary object.
it = m_genRoads->getMultimapData().equal_range(i);
Since getMultiMapData() returns a copy of the multimap, that multimap is gone after the line is executed, thus rendering any iterators invalid.
One solution is to return a reference to the multimap, not a new copy of the multimap.
std::multimap<int, glm::vec3>& GenerateRoads::getMultimapData() { return m_roadsMultimap; }

Related

Removing first three elements of 2d array C++

So here's my problem.. I have a 2d array of 2 char strings.
9D 5C 6S 9D KS 4S 9D
9S
If 3 found I need to delete the first 3 based on the first char.
card
My problem is I segfault almost anything i do...
pool is the 2d vector
selection = "9S";
while(col != GameBoard::pool.size() ){
while(GameBoard::pool[col][0].at(0) == selection.at(0) || cardsRem!=0){
if(GameBoard::pool[col].size() == 1){
GameBoard::pool.erase(GameBoard::pool.begin() + col);
cardsRem--;
}
else{
GameBoard::pool[col].pop_back();
cardsRem--;
}
}
if(GameBoard::pool[col][0].at(0) != selection.at(0)){
col++;
}
}
I've tried a series of for loops etc, and no luck! Any thoughts would save my sanity!
So I've tried to pull out a code segment to replicate it. But I can't...
If I run my whole program in a loop it will eventually throw a segfault. If I run that exact code in the same circumstance it doesn't... I'm trying to figure out what I'm missing. I'll get back in if I figure out exactly where my issue is..
So in the end the issue is not my code itself, i've got memory leaks or something somewhere that are adding up to eventually crash my program... That tends to be in the same method each time I guess.
The safer and most efficient way to erase some elements from a container is to apply the erase-remove idiom.
For instance, your snippet can be rewritten as the following (which is testable here):
using card_t = std::string;
std::vector<std::vector<card_t>> decks = {
{"9D", "5C", "6S", "9D", "KS", "4S", "9D"},
{"9S"}
};
card_t selection{"9S"};
// Predicate specifing which cards should be removed
auto has_same_rank = [rank = selection.at(0)] (card_t const& card) {
return card.at(0) == rank;
};
auto & deck = decks.at(0);
// 'std::remove_if' removes all the elements satisfying the predicate from the range
// by moving the elements that are not to be removed at the beginning of the range
// and returns a past-the-end iterator for the new end of the range.
// 'std::vector::erase' removes from the vector the elements from the iterator
// returned by 'std::remove_if' up to the end iterator. Note that it invalidates
// iterators and references at or after the point of the erase, including the
// end() iterator (it's the most common cause of errors in code like OP's).
deck.erase(std::remove_if(deck.begin(), deck.end(), has_same_rank),
deck.end());
So for anyone else in the future who comes across this...
The problem is I was deleting an element in the array in a loop, with the conditional stop was it's size. The size is set before hand, and while it was accounted for in the code it still left open the possibility for while(array.size() ) which would be locked in at 8 in the loop be treated as 6 in the code.
The solution was to save the location in the vector to delete and then delete them outside of the loop. I imagine there is a better, more technical answer to this, but it works as intended now!
for (double col = 0; col < size; ++col)
{
if(GameBoard::pool[col][0].at(0) == selection.at(0)){
while(GameBoard::pool[col][0].at(0) == selection.at(0) && cardsRem !=0){
if( GameBoard::pool[col].size() > 1 ){
GameBoard::pool[col].pop_back();
cardsRem--;
}
if(GameBoard::pool[col].size() <2){
toDel.insert ( toDel.begin() , col );
//GameBoard::pool.erase(GameBoard::pool.begin() + col);
cardsRem--;
size--;
}
}
}
}
for(int i = 0; i< toDel.size(); i++){
GameBoard::pool.erase(GameBoard::pool.begin() + toDel[i]);
}

Pointer Problems in Vector

I trying to brush up on my C++ since as it has been a while since I used it and I'm having a problem with storing pointers. At the end of the method below, the vector "graph" has all of the vertices I inserts, but the edges that were supposed to be added are corrupt (I can see the edges in the debugger, but their data is garbage). I was wondering if someone could help guide?
Thanks!
For reference, the add_edge function is declared as follows:
std::vector<vertice*> m_edges;
...
...
void add_edge(vertice* n);
{
m_edges.push_back(n);
}
The primary problem:
std::vector<vertice> graph;
std::string line;
//Iterate over line
int lineCount = 0;
while(getline(file, line))
{
auto vertices = parse_line(line);
for(size_t i = 0; i < vertices.size(); ++i)
{
auto result = std::find_if(
graph.begin(),
graph.end(),
[&] (vertice cVert) { return cVert.getName() == vertices.at(i); });
std::vector<vertice>::size_type currPos =
std::distance(graph.begin(), result);
if(result == graph.end())
{
graph.emplace_back(vertice{vertices.at(i)});
}
if(i == 0) { continue; }
graph.at(lineCount).add_edge(currPos == graph.size() ?
&graph.back() : &graph.at(currPos));
}
++lineCount;
}
//The vector graph has corrupt nodes here
Pointers to the content of a std::vector may be invalidated when you insert new elements, such as in std::vector::emplace_back:
If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.
Consider std::vector::reserve to reserve a capacity before needing to reallocate the elements or use different container, one which does not invalidate references on insert, perhaps std::list.
The problem lies here:
auto v = currPos == graph.size() ? &graph.back() : &graph.at(currPos);
You are taking the address of the graph elements, however if (when) your graph vector resizes during the emplace_back, you end up with dangling pointers. A solution is to fix the size of the vector and instead of push_back/emplace_back, to use directly std::vector::operator[], or to reserve enough memory via std::vector::reserve.
Probably unrelated:
graph.emplace_back(vertice{vertices.at(i)});
should just be
graph.emplace_back(vertices.at(i));
as otherwise you are invoking a copy constructor, see the definition of std::vector::emplace_back.

C++ Access Violation Reading location 0x003AE000 when trying to erase from vector

I am getting an error when I try to erase an item from an vector if the item is set to not alive by a boolean. I have tried searching the net but havn't found anything about it. I have tried to find different ways to delete elements on index x in a vector and found the function: vector.erase(vector.begin() + index)
So when I try using it in my for loop I get access violation reading location pointing at the erase function line.
Code for the loop where the error is:
if (!player.getBullets().empty())
{
for (int x = 0; x < player.getBullets().size(); x++)
{
//Check for projectiles whos status is dead.
if (!player.getBullets()[x]->getAlive())
{
//Erase the element at position x.
player.getBullets().erase(player.getBullets().begin() + x);
}
}
}
Don't reinvent the wheel, especially not the really difficult one that can go under water and up walls. Use a ready-made one:
#include <algorithm>
player.getBullets().erase(
std::remove_if(player.getBullets().begin(),
player.getBullets().end(),
[](Projectile * p) -> bool { return !p->getAlive(); }),
player.getBullets().end());
(I'm assuming that Bullet is the same as decltype(player.getBullets())::value_type, i.e. the element type of the bullets container. Adjust to suit.)
It's much easier to erase correctly if you use an iterator instead of an index. An (almost) direct conversion of your code:
vector<Projectile*> bullets = player.getBullets();
for (vector<Projectile*>::iterator x = bullets.begin(); x != bullets.end(); )
{
//Check for projectiles whos status is dead.
if (!(*x)->getAlive())
{
//Erase the element at position x.
x = bullets.erase(x);
}
else
{
++ x;
}
}
Note that this only works with a local copy of the vector. If you want to update the vector in the player class itself, you will need to change getBullets to return a reference:
vector<Projectile*> &Sprite::getBullets()
{
return bullets;
}
And then for the loop:
vector<Projectile*> &bullets = player.getBullets();
Though the loop is invalid because counter x is increased while the size of the vector is decreased and as the result not all the vector is traversed I do not see the reason of the access violation. I think that the problem is related to objects stored in the vector.

Erasing an element from the vector during iteration c++

I wrote this method to find the minor of a sparse matrix:
SpMatrixVec SparseMatrix::minor(SpMatrixVec matrix, int col) const{
SpMatrixVec::iterator it = matrix.begin();
int currRow = it->getRow();
int currCol = col;
while(it != matrix.end()) {
if(it->getRow() == currRow || it->getCol() == currCol){
matrix.erase(it);
}
// if we have deleted an element in the array, it doesn't advance the
// iterator and size() will be decreased by one.
else{
it++;
}
}
// now, we alter the cells of the minor matrix to be of proper coordinates.
// this is necessary for sign computation (+/-) in the determinant recursive
// formula of detHelper(), since the minor matrix non-zero elements are now
// in different coordinates. The row is always decreased by one, since we
// work witht he first line, and the col is decreased by one if the element
// was located after 'col' (which this function receives as a parameter).
//change the cells of the minor to their proper coordinates.
for(it = matrix.begin(); it != matrix.end(); it++){
it->setRow(it->getRow()-1);
int newY;
newY = (it->getCol() > col) ? it->getCol() + 1 : it->getCol();
it->setCol(newY);
}
return matrix;
}
Now, i'm probably doing something wrong, because when reaching the second interation of the while loop, the program crashes.
The basic idea was to go over the vector, and see if it is the relevant coordinate, and if so - to delete it. I increment the iterator only if there was no deletion (and in this case, the vector should update the iterator to be pointing the next element..unless i got these things wrong).
Where is the problem?
Thanks a lot.
erase() invalidates your iterator.
You must update it using the return value of erase() for the loop to work:
while(it != matrix.end()) {
if(it->getRow() == currRow || it->getCol() == currCol){
//matrix.erase(it);
it = matrix.erase(it); // Here is the change
}
// if we have deleted an element in the array, it doesn't advance the
// iterator and size() will be decreased by one.
else{
//it++;
++it; // ++i is usually faster than i++. It's a good habit to use it.
}
}
erase invalidates your iterator. Do it = matrix.erase(it) instead.
You can't change a collection while you're iterating between its elements.
Use another temp collection to store the partial results.
edit: Even better, use a functor to delete elements: remove_if
You write the condition.

How to iterate through map, modify map but restore at each iteration?

I have a std::map<int,int> lets call it my_map
I iterate through this map using iterators and a for loop.
Within each iteration I want to modify many elements in this map but restore it again to its original values for next iteration of the loop.
I thought I could create a temporary copy of the iterator my_temp_map , but then I wouldn't be able to use the iterator to find the element I ought to be working on.
I then thought I could create a temporary copy, work on the origin my_map and at the end of each loop restore the original back to the temporary copy. However I believe this would invalidate the iterators as an assignment deletes all elements
How does one solve this problem?
Code added
So each inner loop will modify current_partition (and there is some more absent code that will store the result of the modified current_partition), but after each inner_loop I need current_loop to be restored to its former self.
std::map<int,int> current_partition = bitset_to_map(centre->second->bit_partitions);
int num_parts = (std::max_element(current_partition.begin(), current_partition.end(),value_comparer))->second;
for (std::map<int,int>::iterator itr = current_partition.begin(); itr != current_partition.end(); ++itr) {
for (int next_part = 0; next_part<num_parts+1; ++next_part) {
if (next_part != itr->second) {
int current_part = itr->second;
itr->second = next_part;
std::vector<int> first_changed_part, last_changed_part;
for (std::map<int,int>::iterator new_itr = current_partition.begin(); new_itr != current_partition.end(); ++new_itr) {
if (new_itr->second == current_part)
first_changed_part.push_back(new_itr->first);
if (new_itr->second == next_part)
last_changed_part.push_back(new_itr->first);
}
}
}
}
I think that std::advance may be of help. Create the temp, then advance begin() until you're where you are now (found out with std::distance)...then whatever it is you're trying to do.
With the code, I understand what you're going for now. I'd do it pretty much the first way you suggest: each time through the outer loop, make a temporary copy of the current_partition data structure, and then work on that, discarding it at the end.
You said that the problem with that would be that you couldn't use an iterator into the original map to find the element you ought to be working on. That's true; you can't do that directly. But it's a map. The element you're working on will have a key which will be the same in any copy of the data structure, so you can use that to create an iterator to the element that you ought to be working on in the copy.
For instance:
std::map<int,int> current_partition = bitset_to_map(centre->second->bit_partitions);
int num_parts = (std::max_element(current_partition.begin(), current_partition.end(),value_comparer))->second;
for (std::map<int,int>::iterator itr = current_partition.begin(); itr != current_partition.end(); ++itr) {
// Make a temporary copy of the map. Iterators between the original and the copy aren't
// interchangeable, but the keys are.
std::map<int,int> tmp_current_partition = current_partition;
// Use the iterator itr to get the key for the element you're working on (itr->first),
// and then use that key to get an equivalent iterator into the temporary map using find()
std::map<int,int>::iterator tmp_itr = tmp_current_partition.find(itr->first);
// Then just replace current_partition with tmp_current_partition and
// itr with tmp_itr in the remainder (hopefully I didn't miss any)
for (int next_part = 0; next_part<num_parts+1; ++next_part) {
if (next_part != tmp_itr->second) {
int current_part = tmp_itr->second;
tmp_itr->second = next_part;
std::vector<int> first_changed_part, last_changed_part;
for (std::map<int,int>::iterator new_itr = tmp_current_partition.begin(); new_itr != tmp_current_partition.end(); ++new_itr) {
if (new_itr->second == current_part)
first_changed_part.push_back(new_itr->first);
if (new_itr->second == next_part)
last_changed_part.push_back(new_itr->first);
}
}
}
}