1

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).

Community
  • 1
  • 1
Mind Smith
  • 31
  • 6
  • There is a lot going on here, and it's awfully hard to read. Can you include a minimal example that illustrates the problem while being as short as possible? – Dan May 27 '13 at 04:23
  • @Dan Thanks for taking the time to look over it! I know it's pretty long. Did you read past the note "_If this is the first time you are reading this all the info you need to answer the question is probably above..._" If so, read the note again ;p I'm pretty sure the error is above that note. Everything else is there just because Jack requested to see it. As for everything above that note, I guess I could try to shorten it more, but it'd be difficult, I already spent a lot of time editing for brevity and a lot of the remaining code (e.g. objects & cout lines) is there for troubleshooting! – Mind Smith May 27 '13 at 04:49
  • Are "::MasterEntityVector" and "Player.MasterEntityVector" the same? They don't look like it. – kfsone May 27 '13 at 04:56
  • Also - have you stepped thru this with a debugger? Using either visual studio or ddd you should be able to watch for changes and narrow it down within a matter of minutes. – kfsone May 27 '13 at 04:58
  • @kfsone My apologies for the confusion, ::MasterEntityVector was a global from OpenGLLoopLogic.ccp, on Jack's advise I changed it to a static member of the CircularEntity class. This did not seem to affect the problem at all (all the cout's are doing the same things in the same places). The problem as stated above still has the original "global from OpenGLLoopLogic.ccp" version of the vector in it not the new "static member of the CircularEntity class". Player.MasterEntityVector is the new version (when I need to access it from OpenGLLoopLogic.ccp). – Mind Smith May 27 '13 at 05:12
  • All references to Player.MasterEntityVector should be down here in the answers/comments or in my Edit/Response to Jack, the original problem above that edit shouldn't mention it. Since the change did nothing to affect the issue (as evidenced by numerous cout's all doing the same thing post-change) just take a look at the original code above the edit. If you agree with Jack that that code is fine and you need to see the rest of the project, but the mixing of the before- and after-change code is too confusing, I will gladly repost the whole thing as one version or another. – Mind Smith May 27 '13 at 05:16
  • I doubt that that is necessary though; I strongly suspect that the issue lies in my original question (somewhere in the four files above the edit/response to Jack) – Mind Smith May 27 '13 at 05:17
  • @Mind Smith: I did read to that point, but that's almost 3 screens of text. A minimal example is usually less than 20 lines. Part of the point of making the example minimal is it makes the question more general: other people may have your exact problem, but they won't have your exact code. – Dan May 27 '13 at 07:14
  • @Dan Now that I have a workaround it probably doesn't matter, but if you are still interested in trying to figure out the _why_ of the problem, take a look at the link in my answer below. That was my problem exactly, but he managed to express it a lot more simply than I did. – Mind Smith May 27 '13 at 18:27

3 Answers3

2

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!

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
Mind Smith
  • 31
  • 6
  • Oh -- that makes it sound like the problem was construction ordering; you had already started using the MasterEntityVector through the constructors of other global objects, when premain finally got around to initializing MasterEntityVector itself. – kfsone May 27 '13 at 18:44
  • @kfsone Yes, I figured it was something like that. I thought it would be okay though because I was accessing MasterEntityVector in the constructor using cout to make sure the vector was there and working. How could I have been doing things like calling .size() to the MasterEntityVector() without it having even been initialized? (and it would always output the correct outputs: for example, with the .size call it would output 1 for 1 object, 2 for 2 objects, 3 for 3 objects...whatever the correct number...until it got reset just prior to main()) – Mind Smith May 27 '13 at 20:20
  • That's the difference between constructed and initialized; the object is constructed - it exists in memory, because being a global ensures it has memory reserved in the data section, but it hasn't been initialized, it just has default blank values. More importantly: It hasn't been initialized **yet** - the application's pre-main is still working it's way thru the todo list. – kfsone May 27 '13 at 20:56
  • Haha I don't like that it does that, but makes sense, thank you very much for the explanation! – Mind Smith May 28 '13 at 23:23
  • Clarification: Obviously, like everything else, it needs to get constructed and then initialized, but I dislike the fact that it is fully functional before getting initialized, and then gets reset without any error or warning. What's the point of initializing something like that when it's fully functional before initialization?!? Either I'm misunderstanding something, or else something here seems more like a bug than a feature... – Mind Smith May 29 '13 at 03:28
0

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.

Jack
  • 131,802
  • 30
  • 241
  • 343
  • Thanks for the quick response! I feel like we are closer to an answer, but the basic problem still remains. I implemented it as follows: //CircularEntity.h: class CircularEntity { public: static std::vector MasterEntityVector; //contains pointers to ALL entities //CircularEntity.cpp: std::vector CircularEntity::MasterEntityVector; //this line is before any functions. I also removed the cout tests 3 and 4 since I realized that moveable objects were going through both constructors and getting added to the vector twice. (continued below) – Mind Smith May 26 '13 at 23:38
  • the same problem still remains. The MasterEntity Vector seems to "reset" and/or get another copy in the same place. The new output is: test 1 0 test 2 1 test 1 1 test 2 2 test 1 0 test 2 1 test 1 1 test 2 2 2 ... – Mind Smith May 26 '13 at 23:40
  • As you can see the "reset" or whatever the problem is is still occurring in the same place it was before, despite the fact that the vector is now a static member of the CircularEntity class rather than being a global from OpenGLLoopLogic.ccp – Mind Smith May 26 '13 at 23:47
  • Then there must be something hidden from the code you posted that we're missing. – Jack May 26 '13 at 23:51
  • If that's the case I apologize for wasting your time; I just added all the files I originally left out. Thank you very much for your continuing help! – Mind Smith May 27 '13 at 00:08
  • Also just added (2nd edit) the complete version of the files in which I originally left code out. – Mind Smith May 27 '13 at 00:20
  • I created a test case to reproduce the problem with no success. So are you sure about the address of `MasterEntityVector` throughout the program? If address is the same the only way you could reset the vector is by assigning a new vector (eg. `MasterEntityVector = vector()`) or by calling `clear()` or `erase()`, I don't see many other ways. So there is still something that we're missing. – Jack May 27 '13 at 06:55
0

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.

neuront
  • 9,312
  • 5
  • 42
  • 71
  • On Jack's advice I made the vector a static member of the CircularEntity class instead of a global from OpenGLLoopLogic.ccp. Didnt change anything as far as results go. 1) MasterEntityVector is referenced elsewhere, but none of its methods are called, its just calls like MasterEntityVector.size() and std::cout << (*Player.MasterEntityVector[n]).xpos << std::endl; Those wouldn't affect anything would they? – Mind Smith May 27 '13 at 04:32
  • 2) The objects of CircularEntity are constructed in OpenGLLoopLogic.ccp. Player and Planet are right at the beginning and the other 2 are in setup(). (good call on that bug, you are right. I dont think its the problem though is it?) 3) No I didnt even make the deconstructor yet. – Mind Smith May 27 '13 at 04:33
  • I tried outputting the address of the vector as you suggested, 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? – Mind Smith May 27 '13 at 04:36