2

In my project, I'm working with about 4 singletons made the Scott Meyer's way. One of them:

LevelRenderer& LevelRenderer::Instance()
{
    static LevelRenderer obj;
    return obj;
}

Now two of those Singletons, LevelRenderer and LevelSymbolTable interact with each other. For example, in this method:

void LevelRenderer::Parse(std::vector<std::string>& lineSet)
{
    LevelSymbolTable& table = LevelSymbolTable::Instance();

    /** removed code which was irrelevant **/

    // for each line in lineSet
    BOOST_FOREACH(std::string line, lineSet)
    {
        // for each character in the line
        BOOST_FOREACH(char sym, line)
        {

            /** code... **/

            // otherwise
            else
            {
                sf::Sprite spr;

                // Used LevelSymbolTable's Instance here...
                table.GenerateSpriteFromSymbol(spr, sym);
                // ^ Inside LevelRenderer

                /** irrelevant code... **/
            }
        }
    }
}

Now, although the problem hasn't occurred yet. The thing I am afraid of is, what if the LevelSymbolTable instance is already destroyed before I call GenerateSpriteFromSymbol ?

Since I used the Scott Meyer way, the Singleton's instance was allocated by the stack. Hence is is guaranteed to be destroyed using the last created first destroyed rule. Now if LevelSymbolTable's Instance is created after LevelRenderer's Instance, it will be destroyed before LevelRenderer's Instance, right? So then, if I call a method of LevelSymbolTable inside LevelRenderer (especially in LevelRenderer's destructor), I will be treading on undefined behaviour land.

As I said before, this problem hasn't actually occurred while debugging, and is purely my assumption and guess-work. So, is my conclusion correct? Is LevelSymbolTable liable to be destroyed before LevelRenderer. If so, is there any way out of this mess?

ApprenticeHacker
  • 21,351
  • 27
  • 103
  • 153
  • 4
    What makes you think a static variable can be destroyed while the program is running? – Mr Lister Jul 07 '12 at 07:25
  • @MrLister what if `LevelRenderer` calls `LevelSymbolTable` while the program is exiting (i.e, in its destructor) and `LevelSymbolTable` is already destroyed? – ApprenticeHacker Jul 07 '12 at 07:27
  • OK, seriously, why would you want to do that? I mean, the one and only time this could happen is after the program stops running (i.e. after the "return" in `main`) and the system is doing its clean up. Are you really trying to do things like `GenerateSpriteFromSymbol` in a _destructor?_ – Mr Lister Jul 07 '12 at 07:31
  • @MrLister I save the sprite's images in files on the disk in the destructor before exiting. For that I need to call `LevelSymbolTable`. – ApprenticeHacker Jul 07 '12 at 07:34
  • 1
    Why not put the code that saves the files on disk in LevelSymbolTable's destructor then? – David Mason Jul 07 '12 at 07:38
  • I would strongly advise against that. That's not what a destructor is for. For one thing, a destructor can't return an error code. – Mr Lister Jul 07 '12 at 07:43
  • 1
    @IntermediateHacker Can't you just make `LevelSymbolTable` a member of `LevelRenderer`? If two singletons depend on each other this way, it might sometimes make sense to define ownership relation between them. – Fiktik Jul 07 '12 at 07:51
  • **Why** a singleton ? It would be *so simple* to instantiate that variable in an `init` routine at the start of the program. Singletons are evil, you're not doing yourself a favor trying to be clever here... in fact, don't forget that you need be twice as clever when debugging than you were when writing code, so you better be conservative else you're just outsmarting your future debugging self. – Matthieu M. Jul 07 '12 at 10:58

4 Answers4

4

You don't have to worry about anything here.* The static keyword guarantees that it is available from when it is initialized to when the program exits. So, you can make a call to a static variable at any point after it has been initialized.

Also, you have a reference to LevelSymbolTable, not a local variable. This is what the ampersand after the class name means. So you can use it locally, but it's really "referring to" the true object which exists somewhere else. So, when the method exits, the reference will go out of scope but the object it refers to will not.

*Well, you may have to worry about one thing. In a destructor, you should just be cleaning up any memory or file references or other things of that nature you have a handle on. I don't know why you would be calling other objects in a destructor.

David Mason
  • 1,545
  • 2
  • 14
  • 14
3

Define ownership relation between the objects. Either have LevelSymbolTable as member of LevelRenderer:

class LevelRenderer {
    LevelSymbolTable symbolTable;
public:
    static LevelRenderer& getInstance();
    ~LevelRenderer() { /* can use symbolTable here */ }
};

Or create one singleton Level that contains both SymbolTable and Renderer:

class Level {
    SymbolTable symbolTable;
    Renderer levelRenderer;   // note the order here
public:
    static Level& getInstance();

private:
    /* have LeverRenderer save reference to symbol table,
       now renderer can use symbol table anywhere */
    Level() : levelRenderer(symbolTable)
    { /* ... */ }
};

EDIT: Or get rid of singletons alltogether. See why singletons are bad. I don't know the structure of your application, but from what I see, you could have Level as a normal class that knows how to render itself and has its symbol table. And have its lifetime connected to the level it is supposed to represent in the application.

Community
  • 1
  • 1
Fiktik
  • 1,941
  • 16
  • 25
1

Static instances will be created at the beginning of the program (before main) and cleaned up at the end (after main), and you can't rely on any particular order in which they are cleaned up. That is, if you have two instances (let's just make them globals for the sake of simplicity)

class one {
  one() {}
  ~one() {}
};

class two {
  two() {}
  ~two() {}
};

one the_one;
two the_other;

int main() {
  ...
  return 0;
}

You cannot and should not make assumptions about the_one being active in the constructor or destructor of the_other. (And vice versa.)

You can, however, rely on them both being active in any other member function, and within main itself.

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
  • Not correct - the order of initialization of global objects within single compilation unit is well defined. You can count on `the_one` being created before `the_other` – Fiktik Jul 07 '12 at 07:42
  • I only put them together here for the sake of simplicity; they are probably in different .cpp files. – Mr Lister Jul 07 '12 at 07:45
  • Yes, but it is still unrelated to OP's situation IMHO. He creates the instances _on first use_ and the language guarantees they will be destructed in reverse order of their creation – Fiktik Jul 07 '12 at 07:48
1

The scenario you raise in your question is unlikely to occur, because Parse is probably being called while the program is still active. Only when the program is about to exit would destructors be called.

In you comments, you indicate a slightly different worry, which is global destructor interdependency. This might actually occur if you have global objects that register themselves with some global container. You might expect that objects would remove themselves from the container, and that the container would eject objects.

One way to deal with this is to allow the container to take ownership of the objects that register with it. This means that what gets registered with the global container are dynamically allocated instances, rather than your Scott Meyer's singleton instances. Then, the global container would take charge of cleaning up the registered items when its global destructor is called.

jxh
  • 69,070
  • 8
  • 110
  • 193