list.remove_if() crashing the program - c++

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();

Related

Calling next multiple times in loop causes stack overflow in ue4 Lua machine

I've been working on lua machine json object system. But I've hit a snag with the json object merger(You can see the functions code used below). When I try to iterate through a table using a while loop and IterateTable function, and then ether call Next or a function containing Next it causes an infinite loop that leads to a stack overflow crash.
Loops through the map elements of a user data object
void UJsonMergetFunctionLibrary::MergeLuaJsonObject(UMyDynamicObject* Origional, UMyDynamicObject* ToMerge, bool Override)
{
//Get all user data object keys
for (TPair<FString, FLuaValue>& P: ToMerge->GetTableContent())
{
//Holds the value of current key
FLuaValue Hold;
if (Origional->hasField(P.Key))
{
if ((P.Value.Type == ELuaValueType::Table) || (P.Value.Type == ELuaValueType::UObject))
{
Hold = Origional->LuaGetField(P.Key);
MergeLuaJsonObject(Hold, P.Value, Override);
}
else if (Override)
{
Origional->LuaSetField(P.Key, P.Value);
}
}
else
{
Origional->LuaSetField(P.Key, P.Value);
}
}
}
Loops through the elements of ether a lua table or user data object
void UJsonMergetFunctionLibrary::MergeLuaJsonObject(FLuaValue& Origional, FLuaValue& ToMerge, bool Override)
{
if (Origional.Type != ToMerge.Type) {
if (Override)
{
Origional = ToMerge;
return;
}
else
{
return;
}
}
//Table
if ((Origional.Type == ELuaValueType::Table) || (ToMerge.Type == ELuaValueType::Table)) {
TPair<FLuaValue, FLuaValue> Pair;
int32 MaxDex = MaxTableIndex(Origional);
while (IterateTable(ToMerge, Pair)) {
if (Pair.Key.Type == ELuaValueType::Integer)
{
MaxDex += 1;
Origional.SetFieldByIndex(MaxDex, Pair.Value);
}
else if (HasTableField(Origional, Pair.Key.ToString()))
{
FLuaValue Hold = Origional.GetField(Pair.Key.ToString());
if ((Pair.Value.Type == ELuaValueType::Table) && (Hold.Type == ELuaValueType::Table))
{
//Hold = OrigionalObj->LuaGetField(P.Key);
MergeLuaJsonObject(Hold, Pair.Value, Override);
}
else if((Pair.Value.Type == ELuaValueType::UObject) && (Hold.Type == ELuaValueType::UObject))
{
MergeLuaJsonObject(Hold, Pair.Value, Override);
}
else if (Override) {
Origional.SetField(Pair.Key.ToString(), Pair.Value);
}
}
else
{
Origional.SetField(Pair.Key.ToString(), Pair.Value);
}
}
}
//Object
else if ((Origional.Type == ELuaValueType::UObject) || (ToMerge.Type == ELuaValueType::UObject)) {
UMyDynamicObject* ToMergeObj = Cast<UMyDynamicObject>(ULuaBlueprintFunctionLibrary::Conv_LuaValueToObject(ToMerge));
UMyDynamicObject* OrigionalObj = Cast<UMyDynamicObject>(ULuaBlueprintFunctionLibrary::Conv_LuaValueToObject(Origional));
TMap<FString, FLuaValue> Keys = ToMergeObj->GetTableContent();
for (TPair<FString, FLuaValue>& P: Keys)
{
FLuaValue Hold;
if (OrigionalObj->hasField(P.Key))
{
if ((P.Value.Type == ELuaValueType::Table) || (P.Value.Type == ELuaValueType::UObject))
{
Hold = OrigionalObj->LuaGetField(P.Key);
MergeLuaJsonObject(Hold, P.Value, Override);
}
else if (Override)
{
OrigionalObj->LuaSetField(P.Key, P.Value);
}
}
else
{
OrigionalObj->LuaSetField(P.Key, P.Value);
}
//OrigionalObj->LuaSetField(P.Key, P)
}
Origional = FLuaValue(OrigionalObj);
}
}
Iterates and retrieves the next key in a table
bool UJsonMergetFunctionLibrary::IterateTable(FLuaValue Table, TPair<FLuaValue, FLuaValue>& Pair)
{
if (Table.Type != ELuaValueType::Table)
return false;
ULuaState* L = Table.LuaState;
if (!L)
return false;
if (Pair.Key.IsNil())
{
L->FromLuaValue(Table);
L->PushNil(); // first key
}
if (L->Next(-2))
{
Pair.Key = L->ToLuaValue(-2);
Pair.Value = L->ToLuaValue(-1);
if (GEngine)
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, Pair.Key.ToString());
L->Pop(); // pop the value
return true;
}
else
{
L->Pop(); // pop the table
return false;
}
}
Checks if the table contains a key
bool UJsonMergetFunctionLibrary::HasTableField(FLuaValue Table, FString FieldName)
{
if (Table.Type != ELuaValueType::Table)
return false;
ULuaState* L = Table.LuaState;
if (!L)
return false;
L->FromLuaValue(Table);
L->PushNil(); // first key
while (L->Next(-2))
{
FLuaValue Key = L->ToLuaValue(-2);
if (Key.ToString() == FieldName)
{
L->Pop(); // pop the value
L->Pop(); // pop the table
return true;
}
else {
L->Pop(); // pop the value
}
}
L->Pop(); // pop the table
return false;
}
1.I have tried poping both the value and the key when calling next within the loop, and than reassigning the old key once the internal next call ends.
2.I have tried googling it and Wasn't able to find anything.
3.I added a limit to how many times the loop can execute, to test if a infinite loops is causing the crash.
4.I tried making a function in pure lua that loops through a table and calls next within it(Didn't work, but do note I'm new to lua).
None of what I've tried has worked. What I want to happen is that when both Original and ToMerge have the same key, and the value assigned to that key is table I want it to perform recursion and merge those two tables than once thats done return to the execution on the original loop. Any help would be appreciated.

Recursive symbol checking

I am getting an error that I am having problems fixing as recursion hasn't "sunk in" yet.
It is supposed to go through an array of symbols already placed by the Class OrderManager Object and check if the symbol passed in is already there or not, if it is not there it should allow the trade, otherwise it will block it (multiple orders on the same currency compounds risk)
[Error] '}' - not all control paths return a value.
I believe it is because of the retest portion not having a return value but again I'm still newish to making my own recursive functions. However it may also be because my base and test cases are wrong possibly?
P.S I added (SE) comments in places to clarify language specific things since it is so close to C++.
P.P.S Due to the compiler error, I have no clue if this meets MVRC. Sorry everyone.
bool OrderManager::Check_Risk(const string symbol, uint iter = 0) {
if((iter + 1) != ArraySize(m_symbols) &&
m_trade_restrict != LEVEL_LOW) // Index is one less than Size (SE if
// m_trade_restrict is set to LOW, it
// allows all trades so just break out)
{
if(OrderSelect(OrderManager::Get(m_orders[iter]),
SELECT_BY_TICKET)) // Check the current iterator position
// order (SE OrderSelect() sets an
// external variable in the terminal,
// sort of like an environment var)
{
string t_base = SymbolInfoString(
OrderSymbol(),
SYMBOL_CURRENCY_BASE); // Test base (SE function pulls apart
// the Symbol into two strings
// representing the currency to check
// against)
string t_profit =
SymbolInfoString(OrderSymbol(), SYMBOL_CURRENCY_PROFIT);
string c_base =
SymbolInfoString(symbol, SYMBOL_CURRENCY_BASE); // Current base
// (SE does the same as above but for the passed variable instead):
string c_profit = SymbolInfoString(symbol, SYMBOL_CURRENCY_PROFIT);
// Uses ENUM_LEVELS from Helpers.mqh (SE ENUM of 5 levels: Strict,
// High, Normal, Low, None in that order):
switch(m_trade_restrict) {
case LEVEL_STRICT: {
if(t_base == c_base || t_profit == c_profit) {
return false; // Restrictions won't allow doubling
// orders on any currency
} else
return Check_Risk(symbol, iter++);
};
case LEVEL_NORMAL: {
if(symbol == OrderSymbol()) {
return false; // Restrictions won't allow doubling
// orders on that curr pair
} else
return Check_Risk(symbol, iter++);
};
default: {
// TODO: Logging Manager
// Hardcoded constant global (SE set to LEVEL_NORMAL):
ENB_Trade_Restrictions(default_level);
return Check_Risk(symbol, iter);
}
}
}
} else {
return true;
}
}
So, I must just have been staring at the code for too long but the problem was the if(OrderSelect(...)) on ln 7 did not have a return case if the order was not properly set in the terminal. I will need to polish this but the following code removes the error.
bool OrderManager::Check_Risk(const string symbol, uint iter=0)
{
if((iter + 1) != ArraySize(m_symbols) && m_trade_restrict != LEVEL_LOW) // Index is one less than Size
{
if(OrderSelect(OrderManager::Get(m_orders[iter]), SELECT_BY_TICKET)) //Check the current iterator position order
{
string t_base = SymbolInfoString(OrderSymbol(), SYMBOL_CURRENCY_BASE); //Test base
string t_profit = SymbolInfoString(OrderSymbol(), SYMBOL_CURRENCY_PROFIT);
string c_base = SymbolInfoString(symbol, SYMBOL_CURRENCY_BASE); //Current base
string c_profit = SymbolInfoString(symbol, SYMBOL_CURRENCY_PROFIT);
switch(m_trade_restrict) // Uses ENUM_LEVELS from Helpers.mqh
{
case LEVEL_STRICT :
{
if(t_base == c_base || t_profit == c_profit)
{
return false;
}
else return Check_Risk(symbol, ++iter);
};
case LEVEL_NORMAL :
{
if(symbol == OrderSymbol())
{
return false;
}
else return Check_Risk(symbol, ++iter);
};
default: {
// TODO: Logging Messages
ENB_Trade_Restrictions(default_level); //Hardcoded constant global
return Check_Risk(symbol, iter);
}
}
}
else {return Check_Risk(symbol, ++iter);}
}
else {return true;}
}

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.

Remove what's in one list from the other list

I have two lists which contain a bunch of elements of the same type:
std::list<Part> allParts = step.getSubParts();
std::list<Part> toRemove;
for (Part part : allParts)
{
for (Part partTwo : allParts) {
if (part.getEdges() == partTwo.getEdges())
if (part.getFaces() == partTwo.getFaces())
if (part.getShells() == partTwo.getShells())
if (part.getVertices() == partTwo.getVertices())
if (part.getWires() == partTwo.getWires())
{
part.addAmount(1);
toRemove.push_back(partTwo);
}
}
}
I have tried iterating through both and remove from the one but I'm constantly getting the list iterators are incompatible error. This is my latest attempt:
std::list<Part>::iterator it;
for (it = step.getSubParts().begin(); it != step.getSubParts().end();)
{
std::list<Part>::iterator i;
for (i = toRemove.begin(); i != toRemove.end();)
{
if (it->getEdges() == i->getEdges())
if (it->getFaces() == i->getFaces())
if (it->getShells() == i->getShells())
if (it->getVertices() == i->getVertices())
if (it->getWires() == i->getWires())
{
it = step.getSubParts().erase(it);
}
else
{
it++;
}
i++;
}
}
Everything I have tried doesn't work. What is the correct way to do this?
You should consider remove_if or erase_if rather than doing your own erase with the hazard of making iterator invalid within a loop.
By the way, you should write predicate like:
if (it->getEdges() == i->getEdges() &&
it->getFaces() == i->getFaces() &&
it->getShells() == i->getShells() &&
it->getVertices() == i->getVertices() &&
it->getWires() == i->getWires()) {
// do something
}
Your code makes people difficult to understand your purpose(at least me).
erase and erase_if
First of all, it would be a good idea to follow the Don't Repeat Yourself principle and write a comparison function for future use:
auto compare_parts = [](const Part& p1, const Part& p2) -> bool {
return ( (p1.getEdges() == p2.getEdges())
and (p1.getFaces() == p2.getFaces())
and (p1.getShells() == p2.getShells())
and (p1.getVertices() == p2.getVertices())
and (p1.getWires() == p2.getWires()) );
}
You would rewrite the first cycle using it and see how much more simple it looks.
Then why not use c++ built-in methods to erase the elements from the list using the function we wrote? This uses new feature in c++ called binding parameters that would aid us here
#include <functional>
using namespace std::placeholders;
for (auto&& badPart : toRemove) {
auto isBad = std::bind(compare_parts, badPart, _1);
step.getSubParts().remove_if(isBad);
}
And that's how you remove special entries from the list.
I think the cleanest way would be:
1. Implement equality operator for the class Part
You can either put it inside or outside the class, it would look like this if you implement it as an external function
inline bool operator==(const Part& lhs, const Part& rhs) {
return lhs.getEdges() == rhs.getEdges() &&
lhs.getFaces() == rhs.getFaces() &&
lhs.getShells() == rhs.getShells() &&
lhs.getVertices() == rhs.getVertices() &&
lhs.getWires() == rhs.getWires();
}
2. Implement the loop, I would recommend using iterators
This is just one way of doing it
if (allParts.size() > 1) {
for(auto partIt = std::begin(allParts); partIt != std::end(allParts); partIt++) {
for(auto partIt2 = std::next(partIt); partIt2 != std::end(allParts);) { // Manual increasing because we erase stuff
if(*partIt == *partIt2) { // Previously implemented equility operator
partIt->AddAmount(1);
partIt2 = allParts.erase(partIt2); // If erase, use the returned iterator as your next `Part`
} else {
partIt2++; // Only increment if nothing was erased (when you erase iterators get invalidated)
}
}
}
}

Iterating over vector of custom object with two conditions

Using C++11, I'd like to iterate over a vector and return a type that indicates that the index was not found.
I am use to the traditional for(;;) loop and specifying the index manually, as my code shows below.
inline std::size_t ItemList::FindItem(Items& Item)
{
for (std::size_t i = 0; i < ItemVector.size(); i++)
{
if (ItemVector[i]->GetId() == Item.GetId() && !ItemVector[i]->GetName().compare(Item.GetName()))
{
return i + 1;
}
}
return 0;
}
I'm also having to increment the index +1 in order to return a value of 0 (to accommodate unsigned size_t) to indicate the calling method that the index was not found (I understand this is asinine). I am assuming it would be more suitable to return something more like std::end()?
Would using a C++11 iterator approach be more efficient? The vector will populate to a large number and the find needs to be quick.
You could use std::find_if and work with iterators:
auto it = std::find_if(ItemVector.begin(), ItemVector.end(),
[&Item](Items *value) {
return value->GetId() == Item.GetId() && !value->GetName().compare(Item.GetName());
}
);
Then you can simply test if it != ItemVector.end() to know if you found something.
There will likely be no (or very small) difference between this and your version in term of speed, but it is a cleaner way to check if something was found or not.
Yes, an iterator would be the way to do this, you're actually writing your own version of find_if You could instead do:
find_if(cbegin(ItemVector), cend(ItemVector), [&](const auto& i){ return i.GetId() == Item.GetId() && i.GetName() != Item.GetName(); })
You can test whether the result of this function was found by testing for equality with cend(ItemVector).
Additionally if you need to find the index of the item you can pass this result after cbegin(ItemVector) to: distance
Live Example
My solution for double search condition that Lambda has multiple parameters in find_if
bool check_second_loop(FullFrame *image_track, guint64 object_id, bool *deletion)
{
auto itr= std::find_if(image_track->track_ids.begin(),
image_track->track_ids.end(),
[object_id](const guint64& a)
{
return a == object_id;
});
if (itr != image_track->track_ids.end())
{
image_track->track_ids.erase(itr);
if(image_track->track_ids.size()==0)
{
*deletion = true;
}
return true;
}
else
return false;
}
bool check_first_loop(guint64 object_id, gint source_id)
{
bool deletion = false;
auto it = find_if(full_frame_list.begin(), full_frame_list.end(),
[object_id, &deletion, source_id](FullFrame &x)
{
return check_second_loop(&x, object_id, &deletion)
&& x.camera_number == source_id;
});
if (it != full_frame_list.end())
{
// Found
return true;
}
else
return false;
}