0

Porting some code from my game engine from Mac to Windows in C++, and I get this runtime error: "Vector erase outside range". It works on Mac!

void Entity::runDeferreds() {
    for (auto it = deferreds.begin(); it != deferreds.end(); /* nothing */ ) {
        if (it->Condition()) {
            it->Execution();

            it = deferreds.erase(it);
        } else {
            ++it;
        }
    }
}

This iterates thru a list of "deferred" tasks, which are stored in a std::vector<DeferredCall> called deferreds. If the DeferredCall's Condition() is fulfilled, then its Execution() is run, and it should be removed from the vector. However, instead, I get the aforementioned error!

DeferredCall looks like this, not that it's too important:

struct DeferredCall {
    std::function<bool()> Condition;
    std::function<void()> Execution;
};

Help?!

EDIT:- Alternative Method

I also tried this, again working on Mac:

deferreds.erase(std::remove_if(deferreds.begin(), deferreds.end(),
    [](DeferredCall &call) {
            if (call.Condition()) {
                call.Execution();
                return true;
            }

            return false;
        }
    ), deferreds.end());

However, in this case I get "vector iterators incompatible".

Javawag
  • 1,547
  • 2
  • 16
  • 23
  • Shouldn't you break out of the loop after erase(it)? Otherwise you are re-iterating over the same erased iterator again. – Dan Simmons Jan 30 '15 at 23:57
  • Do the callbacks have access (even indirectly) to `deferreds`. Could it be modified within a callback? – eerorika Jan 30 '15 at 23:58
  • Alternative method is an implementation of erase remove idiom. It looks fine!! – Steephen Jan 31 '15 at 00:00
  • 2
    @DanSimmons: `std::vector::erase` returns an iterator following the removed element which this code assigns to `it`. This code looks like it correctly implements a common idiom. – Blastfurnace Jan 31 '15 at 00:01
  • 4
    Both of those look ok. Is this your actual code or just an example? The reason I ask is that most of the time when you see the "iterators incompatible" message it's because the vector is being returned by value rather than reference from a get function. – Retired Ninja Jan 31 '15 at 00:02
  • Ah OK yeah in that case I'm at a loss. – Dan Simmons Jan 31 '15 at 00:03
  • 3
    http://stackoverflow.com/help/mcve – Benjamin Lindley Jan 31 '15 at 00:03
  • Could it be that the deferreds object is invalid? for example if deferreds is stored in Entity which is no longer valid (already freed). Add a "bool valid;" member variable in Entity and set it to true in the constructor and false in the destructor. Then check if still true in the runDeferreds function. – andreaplanet Jan 31 '15 at 01:54

1 Answers1

5

Although it doesn't answer where your error comes from, you may try to rework the code as follows:

const auto pred = [](Deferred& d){ return !d.Condition(); };
auto itMatch = std::partition( defs.begin(), defs.end(), pred);

const auto action = [](Deferred& d){ d.Execution(); };
std::for_each(itMatch, defs.end(), action);

defs.erase(itMatch, defs.end());

Also, std::vector::erase is guaranteed to return perfectly valid iterator. Could be vector::end() though.

Links: std::partition, std::for_each, std::vector::erase

sehe
  • 374,641
  • 47
  • 450
  • 633
Igor Korzhanov
  • 216
  • 1
  • 6
  • Good idea. This avoids the undefined behavior of calling a non-const function on the object in the predicate passed to `std::remove_if` – Benjamin Lindley Jan 31 '15 at 00:10