Mind Smith
Mind Smith

Reputation: 31

Global Vector Getting Reset

Update:

As neuront suggested, I tried outputting the address of the vector, it is the same throughout the program. This means that there is no duplication going on; the vector is somehow getting "reset" after Planet and Player are defined but before the program gets to setup() and render(). What could be causing this?

Update 2:

Using cout, I have determined that the error occurs before main(). So the global Player and Planet objects are constructed, and pointers to them are added to the MasterEntityVector. Then, before main() is started (or as main() is started, but before any part of it is executed), the MasterEntityVector gets "reset", after which point everything runs as expected. This behavior occurs both when the MasterEntityVector is a static member of the CircularEntity class and when MasterEntityVector is a global in OpenGLLoopLogic.ccp. What could be causing this behavior? I think maybe it has something to do with the "static initialization order fiasco" http://www.parashift.com/c++-faq-lite/static-init-order.html but the issues seem to be slightly different ones (for example, I'm not getting a crash).

Update 3:

I don't know why it was not working, but I found someone with the same problem and a solution. See my answer below. I'm going to delete the inclusion of the entire project (trim the question back down the its original size) since, looking at Alex's question in the link, the larger project seems to be irrelevant to the problem.


I apologize in advance if my error is trivial. I am a C++ beginner with a relatively poor grasp of the concepts of scope and multiple file projects, and so although I have spent many hours playing with the code and searching the Internet for an answer, I may have missed something obvious.

The below code is simplified for the purpose of making my question easier to answer.

I am using Codeblocks, C++, OpenGL (for the graphics) and SDL (for the window).

cout lines are added for troubleshooting, I shall include the output below.

The problem is a global vector called MasterEntityVector, which is supposed to contain pointers to all "entities" in my simulation. It is declared in CircularEntity.ccp, with an extern in CircularEntity.h. Pointers are supposed to be added to it during the constructors for entities. In OpenGLLoopLogic.ccp, pointers are added to the MasterEntityVector as entities are created, but then when we start getting into the init/setup/render functions it seems to either get reset or else get a second instance of it created. How do I stop this undesirable behavior?

CircularEntity.h:

#ifndef CIRCULARENTITY_H
#define CIRCULARENTITY_H

#include "LUtil.h"

class CircularEntity {
    public:
        CircularEntity(double x, double y, int r);
        double xpos, ypos;
        int radius;
        void Draw(double camxpos, double camypos);

};

extern std::vector<CircularEntity *> MasterEntityVector;  //contains pointers to ALL entities

#endif // CIRCULARENTITY_H

CircularEntity.ccp:

#include "CircularEntity.h"

std::vector<CircularEntity *> MasterEntityVector;  //contains pointers to ALL entities

CircularEntity::CircularEntity(double x, double y, int r) {
    radius = r;
    xpos = x;
    ypos = y;
    std::cout << "test 1" << std::endl;
    std::cout << MasterEntityVector.size() << std::endl;
    MasterEntityVector.push_back(this);
    std::cout << "test 2" << std::endl;
    std::cout << MasterEntityVector.size() << std::endl;
}

...
//irrelevant code removed
...

OpenGLLoopLogic.h:

#ifndef OPENGLLOOPLOGIC_H
#define OPENGLLOOPLOGIC_H

#include "MoveableCircular.h"

//Screen constants
const int SCREEN_WIDTH = 1800;
const int SCREEN_HEIGHT = 1000;

bool initGL();
    
void setup();

void update();
    
void render();
    
void handleKeys( unsigned char key, int x, int y );

#endif // OPENGLLOOPLOGIC_H

OpenGLLoopLogic.ccp:

#include "OpenGLLoopLogic.h"

//The projection scale
GLfloat gProjectionScale = 1.f;
MoveableCircular Player(200, 200, 0, 0, .05, 10);
CircularEntity Planet(0, 0, 100);

bool initGL()
{
    ...
    //irrelevant code removed
    ...
    setup();

    return true;
}

void setup() {
    CircularEntity Planet2(0, 0, 100);
    CircularEntity Planet3(0, 0, 100);
}

void velocityupdate()
{
    Player.Gravity(0,0,100);
}

void positionupdate()
{
    Player.PositionUpdate();
}

void update()
{
        velocityupdate();
        positionupdate();
}

void render()
{
    ...
    //irrelevant code removed
    ...
    for (int n=0; n<MasterEntityVector.size(); n += 1) {
        (*MasterEntityVector[n]).Draw(Player.xpos, Player.ypos);
        std::cout << MasterEntityVector.size() << std::endl;
    }

    ...
    //irrelevant code removed
    ...
}

void handleKeys( unsigned char key, int x, int y )
{
    ...
    //irrelevant code removed
    ...
}

I omitted several files so you all don't have do read through lots of irrelevant code:

The MoveableCircular source and header are quite similar to the CircularEntity files. (cout's tests 3 and 4 instead of 1 and 2, and the MoveableCircular class inherits from CirularEntity, it just has a redefined constructor). The main.ccp calls init, then has a loop: handle keys, update, and then render. The files "above" MoveableCirular.h in the "include tree" (I don't know the correct term) shouldn't have anything to do with the issue, the only thing they do that is really relevant to this problem is "#include "

The output is:

test 1
0
test 2
1
test 3
1
test 4
2
test 1
2
test 2
3
test 1
0
test 2
1
test 1
1
test 2
2
2
2
2
.
.
.
[infinite 2's]

As you can see from the output, everything goes fine as the Player and Planet objects are constructed. However, when we get into the OpenGLLoopLogic functions (Planet2 and Planet3 in setup, the draw code render, etc...) it seems to "reset" or create a second copy of the MasterEntityVector. What is the cause of this undesirable behavior?

Things I've already tried:

Adding "::" throughout the code before MasterEntityVector

Namespace stuff (although my knowledge and understanding of namespaces is admittedly weak, so this still could be the source of the problem).

Upvotes: 1

Views: 2480

Answers (3)

Mind Smith
Mind Smith

Reputation: 31

I still don't know why what I was doing was wrong, but here is a link to an identical problem and a fix for anyone having a similar problem:

Global vector emptying itself between calls?

See ZeRemz's solution.

In my case I used/added the following code:

in CircularEntity.h:

std::vector<CircularEntity *> &getMasterEntityVector();

in CircularEntity.ccp:

std::vector<CircularEntity *> &getMasterEntityVector()
{
    static std::vector<CircularEntity *> s_vector;
    return s_vector;
}

I still don't know why my original implementation (or why Alex's original implementation in the link) is wrong, and I never like not knowing why, but at least we have a solution!

Upvotes: 2

neuront
neuront

Reputation: 9622

I've read almost the codes but still have some questions:

  • In your codes MasterEntityVector is referenced only in the constructor of CircularEntity. Is anywhere else it has been referenced, especially, its pop_back, erase or any non-const methods called?
  • The objects of CircularEntity and its sub-classes, where have they been constructed?
  • You don't have CircularEntity::~CircularEntity overloaded, have you?

To the latter 2 questions, I've found a bug (?) there, in

void setup() {
    CircularEntity Planet2(0, 0, 100);
    CircularEntity Planet3(0, 0, 100);
}

You've 2 CircularEntitys constructed locally, so they will get destructed after setup() called in initGL(). If you correctly write ~CircularEntity, you must have remove this from MasterEntityVector, thus decreasing the size of the vector. (but I didn't see the declaration of ~CircularEntity)

Moreover, I think you could try outputing the address of the global vector, if you doubt whether there's another instance of it.

Upvotes: 0

Jack
Jack

Reputation: 133587

Just forget about the extern variable out of any namespace and use a static variable inside CircularEntity class (or another class, like Entities).

//CircularEntity.h:
class CircularEntity {
  public:
    static vector<CircularEntity*> entities;
}

//CircularEntity.cpp
vector<CircularEntity*> CircularEntities::entities;

...
CircularEntities::entities.push_back(whatever);

So that everything will be even more encapsulated.

Upvotes: 0

Related Questions