0

I asked this question about a half hour ago, but the code had typos and I wasn't being very clear, so I've deleted and I'm trying again with a better format.

I'm getting a Segmentation Fault in my code, the problem seems to be at the function call if( (*trans).test((*this), *(*itr)) ) inside World::update():

void World::update(sf::Time dt)
{
    mPlayer->setVelocity(0.f, 0.f);

    while (!mCommandQueue.isEmpty()){
                Command cmd = mCommandQueue.pop();
                cmd.action(cmd.node, dt);
    }
    mSceneGraph.update(dt);
    adaptPlayerPosition();

    //Enemy Spawn engine
    if (mSpawnTimer.getElapsedTime().asSeconds() >= SPAWN_INTERVAL && mEnemies.size() < MAX_NUM_ENEMIES){
        float winX = mWindow.getDefaultView().getSize().x;
        float winY = mWindow.getDefaultView().getSize().y;
        float x = rand() % (int)winX;
        float y = rand() % (int)winY;
        spawnEnemy(x, y, EnemyType::Loner, IState::ILWander);
        mSpawnTimer.restart();
    }

    // FSM update
    IState::ID curEnemyStateID;
    FState curEnemyState;
    bool trigger = false;
    ICondition triggeredtrans;
    FState triggeredState;
    for(auto itr = mEnemies.begin(); itr != mEnemies.end(); ++itr){
        curEnemyStateID = (*itr)->getState();

        // set curState to whatever the enemy's curState is
        switch(curEnemyStateID){
        case 0:
            curEnemyState = LWander;
            break;
        default:
            break;
        }

        auto tState = curEnemyState.getTransitionStates().begin();
        for(auto trans = curEnemyState.getConditions().begin(); trans != curEnemyState.getConditions().end(); ++trans){
            if( (*trans).test((*this), *(*itr)) ){
                trigger = true;
                triggeredState = (*tState);
                break;
            }
            ++tState;
        }
        if(trigger){
            (*itr)->setState(IState::ILRushPlayer);
            curEnemyState = LRushPlayer;
        }
        curEnemyState.getAction()->doAction((*this), *(*itr));
    }
}

Context:

trans is an iterator for a std::vector<ICondition> conditions where each ICondition has a test(World& world, Enemy& enemy). itr is an iterator through a std::vector<Enemy*> that is held by World.

The conditions vector is filled in this function:

void World::initializeStates()
{
    Wander LWanderAction;
    LWander.setAction(LWanderAction);

    DistFromPlayer LWanderCond1(30);

    LWander.pushCondition(LWanderCond1);
    LWander.pushTransitionState(LRushPlayer);
}

LWander is a state (FState) in my Finite State Machine. Wander is a class that inherits IAction, and setAction accepts an IAction parameter: FState::setAction(IAction iact)

DistFromPlayer is a class that inherits ICondition.

FState::pushCondition(ICondition icond) and FState::pushTransitionState(Fstate state) should take their arguments and push them to Fstate's conditions and states vectors. (A transition's condition and matching target state should be at the same indices in both)

LWander and LRushPlayer are both members of World.


And that should cover everything. I don't know why I'm getting a SegFault, but I'm assuming that the problem is with how things are pushed into LWander in World::initializeStates(). I should also note that the SegFault occurs right after the first enemy is spawned in my game, which is also the same update frame that runs (*trans).test(*this, **itr) for the first time. All Enemys start in the LWander state.


ICondition's virtual bool test(World& world, Enemy& enemy); is defined as:

bool ICondition::test(World& world, Enemy& enemy){
    return false;
    //returns false by default, overwritten later
}

and DistFromPlayer's bool test(World& world, Enemy& enemy); is defined as:

bool DistFromPlayer::test(World& world, Enemy& enemy)
{
    std::cout << "DistFromPlayer (LWander's Transition) was reached\n";
    return false;
}

and only contains a print statement for debugging purposes.


GDB's backtrace

#0  World::update (this=0x6464408, dt=...) at C:\...\World.cpp:97
#1  0x0040416b in GameState::update (this=0x64643f0, dt=...) at C:\...\GameState.cpp:22
#2  0x00402435 in StateStack::update (this=0x28fde0, dt=...) at C:\...\StateStack.cpp:19
#3  0x00403782 in Game::update (this=0x28fbc0, elapsedTime=...) at C:\...\Game.cpp:58
#4  0x004036a2 in Game::run (this=0x28fbc0) at C:\...\Game.cpp:48
#5  0x0040888b in main () at C:\...\main.cpp:7
  • 1
    How do you know that "the problem *seems* to be at this function call"? Have you run your program in a debugger to find out? If so, what's the function call stack? What is the values of involved variables? – Some programmer dude Dec 05 '13 at 08:20
  • @JoachimPileborg I ran it through Code::Block's Debugger, and the function stack is main()->Game::run()->Game::update()->StateStack::update()->GameState::update()->World::update()-> ??() Which is exactly what I expect it to be. `(*trans)` should be set to the first element in `LWander.conditions`, which should be of type `public DistFromPlayer : public ICondition` and `**itr` should be of type `public Enemy` – user3051585 Dec 05 '13 at 08:26
  • This screams 'dereferencing an invalid iterator' to me... are you modifying the vectors for which trans and itr are iterators at any point in the loop? It might even be helpful to see all of the World::update() function while you're at it, if it's not absurdly large – Commander Coriander Salamander Dec 05 '13 at 09:04
  • @Commander I haven't changed the trans or itr iterators' vectors, at any point during the loops. I edited the post to show the whole `World::update()` – user3051585 Dec 05 '13 at 19:50
  • I also added a `if (trans != curEnemyState.getConditions().end() && itr != mEnemies.end())` and it passed, so it looks like they are both valid iterators. – user3051585 Dec 05 '13 at 19:58
  • Hmm. What about that `auto tState = curEnemyState.getTransitionStates().begin();` immediately before your loop? There's a `++tState;` within that loop, and you never check whether your `tState == curEnemyState.getTransitionStates().end()` at any point - do you possibly advance that iterator past the end of its vector without realizing? – Commander Coriander Salamander Dec 05 '13 at 21:09
  • Also, you're storing a bunch of objects that inherit from ICondition by value in a vector of IConditions - sounds like you might be a victim of http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c – Commander Coriander Salamander Dec 05 '13 at 21:13
  • @Commander The purpose to that line is having an iterator running through `states` parallel to the iterator going through `conditions`. Both vectors should have the same number of elements, and shouldn't it not even matter if the segfault happens before any call to tState? – user3051585 Dec 05 '13 at 21:16
  • You may be right on that - as long as you're not touching those vectors anywhere that you don't show here, they should be in sync enough for you to do that, though it's riskyyyy. Anyways, second problem - you push back `LWanderCond1` onto LWander's `conditions()` vector, which is pushing back a derived object into a container of its parents - meaning you could be deleting all of the member variables and functionality of the derived class without knowing (as you stuff the object into a too-small container) then trying to access those members. You might need vectors of pointers instead. – Commander Coriander Salamander Dec 05 '13 at 21:23
  • @Commander I've made the change: `std::vector conditions` and `(*trans)->test((*this), *(*itr))` But I seem to still be getting the SegFault, so I guess those problems weren't related. – user3051585 Dec 05 '13 at 21:47
  • Could you run your program with gdb (or other debugger) and -O0 (no optimization) and give us backtrace? – klm123 Dec 05 '13 at 23:06
  • @klm123 I added the function stack in the second comment, but Ill put the full GDB >bt output to the original question. – user3051585 Dec 05 '13 at 23:12

1 Answers1

0

I am suspecting tState iterator. You only initialize it with begin() and increment it. I can't find any test against end() of the appropriate container. Or is it safe because of some relation between curEnemyState.getTransitionStates() and curEnemyState.getConditions() which I don't realize?

Jan Korous
  • 666
  • 4
  • 11
  • the `conditions` and the `states` vectors should have the same length, where one condition's target state is kept in the same index in `states` as the condition is in `conditions`. Therefore if I increment them together, then I have access to a condition and it's target state. However, this code is not related to the segfault, as the program crashes before the first call to `tState`. – user3051585 Dec 05 '13 at 22:14
  • Ok, get it. It might be useful to know exact source of segfault. I am not sure about your platform - could you use valgrind (if on linux) or some alternative (if on windows)? – Jan Korous Dec 05 '13 at 22:24
  • Sorry, but I'm kind of new to programming, let alone using a debugger. What do you mean by source of segfault? According to Code::Blocks, which uses GDB I think, the program crashes on `(*trans).test((*this), *(*itr)) )` as I stated. if you mean which of those pointers is causing it, then I'm pretty sure `(*this)` isn't the problem, and I'm trying to find out if perhaps when I fill `conditions` with `IConditions` or `mEnemies` with `Enemies`, maybe I'm doing it incorrectly, so the iterators wont be working properly. I created a new question asking if I'm filling `mEnemies` correctly. – user3051585 Dec 05 '13 at 22:41
  • I see. Could you then provide definition of that ICondition::test() method? – Jan Korous Dec 05 '13 at 22:48
  • I added it to the bottom of the original post. If you had time and so desired, we could commune over a VoIP also. – user3051585 Dec 05 '13 at 23:00