TITLE: destruction of singletons followup

FROM: jamshid, 5 Mar 96

>From: Jamshid Afshar <jamshid@io.com>
Newsgroups: comp.lang.c++.moderated
Subject: Re: Destruction of singletons
Summary: simple idiom to require one singleton to "live" longer than another

In article <4h3gkn$f3k@netlab.cs.rpi.edu>,
Jody Hagins <gamecox@magicnet.net> wrote:
>jamshid@io.com (Jamshid Afshar) wrote:
>>With the committee leaving destruction order of local statics
>>unspecified, you'll have to instead dynamically allocate the singleton
>>and use atexit() to delete it if you have singletons referring to each
>>other in their constructor or destructor code. [...]

Btw, Jame Kanze <kanze@gabi-soft.fr> corrected me in comp.std.c++ --
the ARM does not leave the destruction order of local statics
unspecified (like non-local statics, they are destroyed in the reverse
order of construction).  Furthermore, he said the latest Draft
specifically addresses the question I had: what's the destruction
order when the constructor of one local static calls a function which
itself has a local static?  Here's the idiom in question:

	static Foo& Foo::singleton() {
	   static Foo the_one;  // what if Foo::Foo() calls Bar::singleton()?
	   return the_one;
	}

The Draft now specifies that destruction happens in the reverse order
the statics' construction was *completed*. So, no matter which order
the singleton() functions are called, the local static in
Bar::singleton() will be destroyed *after* Foo::singleton()'s.  This
means it's safe to use Bar::singleton() in Foo's destructor.  When
using this idiom, just make sure that if you call a singleton()
function during the destructor of another singleton, there is a
corresponding call in the constructor(s) (even if the result is
unused).

The appended program code uses this simple idiom.  You can use the
program to test that your compiler follows the new Draft spec. VC++4
seems to, though you will have to workaround some access-related
compiler bugs to compile the code.

Btw, this idiom only handles "one-way" use relationships between
singletons.  If you have two singletons which need to "know" about
each other you may be able to use the more complicated idiom below and
return a pointer so that you can test if the singleton has already
been destroyed (presumably that case has to be handled, as opposed to
being a programming error).  You may also need to use the below idiom
if your singleton has special requirements (eg, it needs to be
reconstructed during the program run).  I believe you can even safely
mix the two idioms in one program since statics initialized before an
atexit() call are required to not be destroyed until after the
registered function is called.

>>	class LogFile {
>>	public:
>>	   LogFile& theLog() {
>>	      if (!p) {
>>	         p = new LogFile;
>>	         atexit(&destroy);
>>	      }
>>	      return *p;
>>	   }
>>	private:
>>	   static LogFile* p;
>>	   void destroy() { delete p; }
>>	};
>
>I don't believe this will work properly.  For it to work, wouldn't
>destroy() need to be static.  Otherwise, the object pointer (which
>would be the only parameter to the actual function) would be
>uninitialized since destroy() is a member function, and it is being
>called as a callback.

That's correct -- I should have checked for typos.  The above code
causes a compiler error unless you make destroy() static.

Jamshid Afshar
jamshid@io.com

- ---clip---
#include <iostream.h>

class LogFile {
  // private ctor&dtor to prevent others from instantiating a LogFile
  LogFile() { cout << "Constructing LogFile" << endl; }
  ~LogFile() { cout << "Destroying LogFile" << endl; }
public:
  static LogFile& global() {
    static LogFile l;
    return l;
  }
  void output( const char* msg ) { cout << msg << endl; }
};

class Loner {
  Loner() { LogFile::global().output("Constructing Loner"); }
  ~Loner() { LogFile::global().output("Destroying Loner"); }
public:
  static Loner& global() {
    static Loner l;
    return l;
  }
};

int main() {
  // No matter which order the next two statements are in, the LogFile()
  // is required by the latest Draft to be destroyed last.
  // 	Constructing LogFile
  // 	Constructing Loner
  // 	Destroying Loner
  // 	Destroying LogFile
  Loner::global();
  LogFile::global();
}

