I have a map that contains a string to sprite association. This function is used to return the sprite to draw on the screen. Here is the function:
sf::Sprite LoopSprite()
{
for (std::map<std::string,sf::Sprite>::iterator it=SpriteMap.begin(); it!=SpriteMap.end(); ++it)
{
return it->second;
}
}
I used to have these in a vector which made loops for drawing easy, but I wanted to use a map to allow easier recognition for maintaining code. The function above only allows one image from the map to be drawn. Is there something wrong with this function?
sf::Sprite LoopSprite(int element)
{
return vec[element];
}
As pointed this works because it takes an element so there is only one return, but I want the same result except with the map. The issue is that it is easy to send in a 0-vec.size. My only idea is to have a vector of strings to allow easy looping.
Your function will simply return in the first iteration, returning only the first element in the map.
You say in the comments that originally, when you were using a vector, the function took an int argument and returned that element. If you still want the function, you can achieve the same with a map like so:
sf::Sprite LoopSprite(std::string key)
{
return SpriteMap[key];
}
If you want to iterate over the elements of the map inside LoopSprite, then you'll need to move the lines of code that actually do something with each element into the function:
void LoopSprite()
{
for (std::map<std::string,sf::Sprite>::iterator it=SpriteMap.begin(); it!=SpriteMap.end(); ++it)
{
// Do something with it->second
}
}
First of all you are returning local variable which is not allowed. Even after returning from the function it may be null or invalid.
You are only returning this part SpriteMap.begin() which is actually useless. This code is much easier.
sf::Sprite LoopSprite(string key)
{
std::map<std::string,sf::Sprite>::iterator it = SpriteMap.begin();
while(it!=SpriteMap.end())
{
if(check) // check if this is your desired key
return value;
++it;
}
return null;
}
map is used usually in a simple way:
string key = "keyItem";
sf::Sprite ans = SpriteMap[key];
Related
Sorry I can't paste the full code but I will try my best to explain the problem. I have a vector of structure. I am passing the vector to a function which fills the vector with structure and returns the vector. But when I try to access the vector elements, I get only the last element inserted. I think the problem is somewhere that I am storing the address of the structure and hence vector only retains the last value but i am not sure how to correct this.
Here is my structure:
struct NA
{ element1;
element2;
};
Here is how I pass my vector after declaring it:
Vector<NA> del;
func(del);
Here is my function: ( q is a variable having results from a stored procedure
func(Vector<NA> &dels)
{
NA& del(*new NA);
while(q.nextquery())
{
while(q.nextrow())
{
q.bind(del.element1);
q.bind(del.element2);
dels.insert(&del);
}
return dels.entries()
}
NA& del(*new NA);
while(q.nextquery())
dels.insert(&del);
Obviously, you are creating only one del object, but you insert it many times in the same vector. At the end, all you vector's entries will point to the same object.
What you want is probably to create a new del object for each entry in your vector. therefore, put the creation statement inside the loop.
func(Vector<NA> &dels)
{
while(q.nextquery())
{
while(q.nextrow())
{
NA& del(*new NA); // <-- Here
q.bind(del.element1);
q.bind(del.element2);
dels.insert(&del);
}
return dels.entries()
}
func() is pushing multiple copies of a single pointer to a single dynamically allocated object into the vector. Each loop iteration is modifying that same object, so every entry in the vector will end up pointing to the data of the last query record that was read.
Try something more like this instead:
func(Vector<NA> &dels) {
while(q.nextquery()) {
while(q.nextrow()) {
NA *del = new NA;
q.bind(del->element1);
q.bind(del->element2);
dels.insert(del);
}
}
return dels.entries();
}
On the other hand, why are you storing pointers in the vector? And using a non-standard vector class? It would be safer to store actual objects into a standard std::vector class instead:
void func(std::vector<NA> &dels) {
while(q.nextquery()) {
while(q.nextrow()) {
NA del;
q.bind(del.element1);
q.bind(del.element2);
dels.push_back(del);
}
}
}
I cannot call a function that does a push_back into a vector
void GameState::InitialiseBullet(float x, float y, float vx, float vy)
{
Bullet* bullets = new Bullet();
bullets->SetSize(5.f, 20.f);
bullets->AddFrame("./images/bullet.png");
bullets->Play();
bullets->SetX(x);
bullets->SetY(y);
bullets->velocityX = vx;
bullets->velocityY = vy;
bullets->isActive = true;
gameObjects.push_back(bullets);
}
when it is inside the following for loop
for (auto& object : gameObjects)
{
//Determine the type at runtime
if (dynamic_cast<Player*>(object) != 0)
{
//Process player-specific logic
PlayerLogic(dynamic_cast<Player*>(object), a_fTimeStep);
}
//Determine the type at runtime
if (dynamic_cast<Bullet*>(object) != 0)
{
//Process bullet-specific logic
BulletLogic(dynamic_cast<Bullet*>(object), a_fTimeStep);
}
if (dynamic_cast<Enemy*>(object) != 0)
{
//Process enemy-specific logic
Enemy* enemy = dynamic_cast<Enemy*>(object);
EnemyLogic(enemy, lowerAliens);
if (enemy->GetIsActive() == true)
{
allDead = false;
}
}
//Update and draw our objects
object->Update(a_fTimeStep);
object->Draw();
}
The piece of code that calls the function:
if (createBullet == true)
{
InitialiseBullet(bulletX, bulletY, 0, 500);
createBullet = false;
}
That code works when outside the for loop. However, I need the for loop to provide access to each of my player, enemy and bullet objects. Is there a way to push_back to a vector inside a for loop that is based on the same vector? I get a "Expression: Vector iterators incompatible" error when it's inside the loop. Any ideas? New to C++ programming.
It looks like you are pushing into the same vector you are iterating, that means, you are forcing items realocation and iterator invalidation; in other words - your data moves to different location and used iterator becomes invalid.
I rarely see situation where you really need to iterate and append same vector, so take a look into your code again.
If you really need to do that, iterate this way:
for (size_t i = 0; i < gameObjects.size(); ++i)
{/*Some code*/}
Also using this method you should use gameObjects[i]. instead of it->
It's just a vector of pointers, so it's not very big.
The objects being added is probably even smaller.
You could make a copy of the vector and iterate over the copy while inserting into the real one.
You could put new items into a new, empty vector while you iterate, and then splice them onto the real one at the end.
To delete objects, you could do either of those things, or you could simply set a flag "isZombie" and then remove all the zombies at the end.
These aren't the only answers, but they all work.
When using iterators to loop through your vector you can't in this 'for-loop' modify the vector.
A quick google gave me this; which seemd to fit your case pretty well.
Probably because the push_back ... caused an internal
reallocation in the vector thus all its iterators were invalidated.
Source: http://www.cplusplus.com/forum/beginner/64854/
Do I understand you right when I'm assuming your using iterators due to your error message.
One question you should ask yourself is why you would ever want to add instances to this vector, maybe you should rethink your design slightly to avoid this.
I been stuck here for a few days. Could anyone take a look at the toString method in the Row class, how can I retrieve every point from a tree of points and get the char value from the point? I know in vector or array we can probably use a for loop and do something like find(points[i]), but I really have no clue here.
**std::string toString() const
{
string s;
for(int i = 0; i != points.size(); i++)
{
//Maybe use points.find(???) here to retrieve the point
s.push_back(point.getType()); //Just for demonstration, this does not work
return s;
}
}**
};
So I cannot modify the binNode binTree class. And I cannot have the print method in the Point class, because the print() method print every element in a new line. But we have to retrieve the char value from every point, and attach all char value of all point into a String.
Thank you so much for giving me a hint!
The binNode template doesn't really support that sort of usage because it doesn't expose it's children. Nor does it supply an iterator. That makes it impossible to traverse the tree in code that is outside the class. The children are not even visible to subclasses so you can't extend it that way.
You could get hacky and specialize the binNode template for Point and force it to emit output into a global string:
std::string output; // a global
template <>
class binNode<Point> {
void print() const {
if (left != NULL) left->print();
output += nodeData.getType();
if (right != NULL) right->print();
}
};
// in some function
output.clear(); // remember to clear previous output
points.print(); // the string now has the output from `print`
This will of course not work if you have multiple threads calling print even on separate bintrees.
To properly implement such functionality, your only option is to modify bintree and binNode.
Okay, I have a STL list of references I am iterating through. This function has three equivalent parts. The function takes a wstring as a parameter, and runs the appropriate if statement. I have reduced the code to one if statement to try and get it working.
So, I check to see what has been passed in as an argument. I then check to see if the ClassItem is a certain type of animal. If it is, I check if it is hungry, and erase it from the list. I am just trying to avoid seg faults right now, and cannot seem to do it.
list<ClassItem *>::iterator i = Items.begin();
while(i != Items.end())
{
if(!AnimalType.compare(L"tiger"))
{
if((*i)->IsAnimalType(L"tiger"))
{
if((*i)->IsHungry())
{
i = Items.erase(i);
}
}
else
{
++i;
}
}
// I have tried removing this
else
{
i++;
}
}
I was under the impression that the current iterator is invalidated when I call erase. So, if I erase an element, I return the next valid iterator. Where am I going wrong?
EDIT: Thank you for all the quick help. The problem has been fixed. I have made use phresnel's solution and it worked wonderfully.
You are better off by using std::list::remove_if with a suitable predicate. This avoids the manual loop entirely, reducing scope for errors, and helping to either remove or at least you localise the source of the problem, since you can rely on this idiom being correct as long as your predicate is.
bool badAnimal(ClassItem * item)
{
// return true if animal is to be removed
}
Items.remove_if(badAnimal);
I see no potential for a segfault here. Anyways:
There are (IMHO) two possible problems:
if(!AnimalType.compare(L"tiger"))
This looks fishy. What is AnimalType? Do you really expect the value of if(!AnimalType.compare(L"tiger")) to change during iteration, if AnimalType itself does not?
In any case, it looks like a read, therefore shouldn't write. It looks constant, therefore shouldn't change.
Then:
if((*i)->IsAnimalType(L"tiger"))
{
if((*i)->IsHungry())
{
i = Items.erase(i);
}
// NO ITERATION IN CASE OF NOT HUNGRY.
// ONCE TRAPPED HERE, YOU HAVE AN INFINITE LOOP,
// EXCEPT AnimalType.compare(L"tiger") DOES SOMETHING
// NON-SANE.
}
else
{
++i;
}
this should better be:
if((*i)->IsAnimalType(L"tiger") && (*i)->IsHungry())
{
i = Items.erase(i);
}
else
{
++i;
}
However, even better would be to use the standard algorithms for element removal.
you may want to add
continue;
after your erasion.
I know how to pass a vector to a function, but how do I pass a vector index to a function, or at least specify which index the function is modifying. For example, I'm working on a Car class and it has a vector if wheel pointers and in order to remove one of the wheels my function looks like this:
Wheel& remove() {
for (int i = 0; i < wheels.size(); i++) {
if (wheels[i].position == wheels.at(i)) {
??
}
what do I need to pass to the function in order to specify which wheel I want removed? When a wheel is removed, the position where it was is still there and can be filled by another wheel. Let's say for example the car had 4 wheels...if I wanted to remove the 2nd index in the wheel vector, what does the function argument for remove() need to take in order to do it? Should I pass in the vector and then the specific index....and if so, what does the syntax look like?
You can just pass an integer to specify which one you would like to remove
void RemoveWheel(int i)
{
if( (i<wheels.size()) and (i>=0) )
wheels.erase(wheels.begin()+i);
}
http://www.cplusplus.com/reference/stl/vector/erase/
If you want to leave the space for another wheel than you should define wheels as a vector of pointers and just delete object at the i-th position and save NULL instead of it.
vector<Wheel *> wheels;
void RemoveWheel(int i)
{
if( (i<wheels.size()) and (i>=0) ) {
delete wheels[i];
wheels[i] = 0;
}
}
Your question is not entirely clear to me, but to remove an element from a vector, given an index i, you can do this:
wheels.erase(wheels.begin() + i);
But this would be better:
auto e = std::remove_if(wheels.begin(), wheels.end(),
[](const Wheel & wheel) {
return wheel.position == wheel;
});
wheels.erase(e,wheels.end());
Although I'm not sure if you want to remove every element that fits that criteria, or just the first. If you would show the logic in pseudocode of what you want to do, that would help.