Advertisement

std::set and modifying its elements

Started by August 01, 2006 10:18 PM
2 comments, last by GBGames 18 years, 3 months ago
I found some code at GPhysics to do "quick and simple" physics, but I find that the code was written in VC++. I've managed to get it to mostly compile in g++ 4.0.4, but I'm having trouble with the use of std::set. The code defines an Arbiter as the decision-maker between two Body objects. It keeps the Arbiter objects in std::set<Arbiter> arbiters. It can find out if an arbiter already exists in the set, but then the code wants to run some operations on each arbiter. None of the operations are const, and they can't because they change the members. The problem is that the ArbIter is a typdef to set<Arbiter>::iterator, which in g++ just ends up being a const_iterator anyway. It makes sense since a set is supposed to have immutable members. Then I found http://www.velocityreviews.com/forums/t287950-which-type-should-quotstdsetbegin-constquot-return.html which seems to suggest that iterator shouldn't be implemented as const_iterator. Is g++ wrong and VC++ correct in this regard? If not, how would I do something like the following: for (ArbIter arb = arbiters.begin(); arb != arbiters.end(); ++arb) { (*arb).PreStep(inv_dt); } Do I really need to change Arbiter's members to mutable? I was thinking about erasing the arbiter from the set, using const_cast, and then inserting it again, which should be fine except that using const_cast makes me nervous. I figure there might be a better solution that is currently escaping me. Then again, it only makes me nervous because I've never used it and I always read that I shouldn't. Perhaps this is one of those situations when its use is valid?
-------------------------GBGames' Blog: An Indie Game Developer's Somewhat Interesting ThoughtsStaff Reviewer for Game Tunnel
Ok, it turned out to be easier than I thought it would be. At least the following compiles:

    for (ArbIter arb = arbiters.begin(); arb != arbiters.end(); ++arb)    {        // Erase key from set, modify it, then add it again.        Arbiter newArb = *arb;        arbiters.erase(arb);        newArb.PreStep(inv_dt);        arbiters.insert(newArb);        //(*arb).PreStep(inv_dt);    }


Now I just need to see if I can use it as there might still be run-time errors.
-------------------------GBGames' Blog: An Indie Game Developer's Somewhat Interesting ThoughtsStaff Reviewer for Game Tunnel
Advertisement
I'm not sure which compiler is more technically correct. But as a design thought, perhaps an arbiter should be split up into two pieces: The key, and the data that can be changed without affecting the key. Then you can put that into a std::map instead of a std::set, and be able to change the data without touching the key, and without ever having to worry in the future that someone (even yourself) might accidentally change the key without realizing (or remembering) it's bad.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Actually, I learned that neither compiler is wrong.

Effective STL by Scott Meyers says that implementations of sets can vary without violating the Standard. In any case, to safely modify the key in a set:

    ArbIter arb = arbiters.begin();    while (arb != arbiters.end())    {        // Erase key from set, modify it, then add it again.        Arbiter newArb((*arb).body1, (*arb).body2);        newArb.PreStep(inv_dt);        arbiters.erase(arb++);        arbiters.insert(newArb);        //(*arb).PreStep(inv_dt);    }


Note that when you erase, the iterator needs to be incremented to stay valid. I changed from a for loop to a while loop to make it easier to work with the iterators in this way.
-------------------------GBGames' Blog: An Indie Game Developer's Somewhat Interesting ThoughtsStaff Reviewer for Game Tunnel

This topic is closed to new replies.

Advertisement