Hi
i've just come across a really unpleasant problem - static destructors.
what i want is to call an "exit" function after "everything". this means, after all calls to static destructors.
i've tried the atexit C runtime library function, which adds a function pointer to a LIFO exit routine stack, but apparently MS VC++ (2005, havent tried other versions yet) uses the same 'atexit' exit routine stack to push a compiler generated function that calls static destructors - and this routine appears to be added somewhere during CRT initialization, assuring it definitely gets called after my custom exit routine.
any ideas?
c++ static destructor sequence
Started by ElOmmy, Feb 09 2006 03:07 PM
7 replies to this topic
#1
Posted 09 February 2006 - 03:07 PM
#2
Posted 09 February 2006 - 03:34 PM
you can force a clean up of all static destructors via _cexit().
The function returns after it has finished (unlike the other incarnations of exit which terminate your app).
ALex
The function returns after it has finished (unlike the other incarnations of exit which terminate your app).
ALex
#3
Posted 09 February 2006 - 04:51 PM
What I do to circumvent this problem is that I have a base class for all classes that need static destruction (and also static construction).
Derived classes override the init() or uninit() method (or both), but contain no code (except variable initialisations) in their constructor. The actual code (like memory allocation/deallocation) has been moved to init()/uninit()
CPP file:
This ensures all classes get initialized/deinitialized in a specific order. Since I use a lot more singletons than init_types, some of them still get called in a random order, but I can make sure that they don't depend on each other. As an example, I don't care in which order my INIT_TYPE_DLLS-type classes get called, but I care that they're called after the memory manager is initialized.
Destruction happens in reverse order, of course.
enum {
I4_INIT_TYPE_MEMORY_MANAGER, // main i4 memory manager
I4_INIT_TYPE_PRIORITY, // Anything that only relies on memman.
I4_INIT_TYPE_THREADS, // initialized thread info
I4_INIT_TYPE_STRING_MANAGER,
I4_INIT_TYPE_FILE_MANAGER,
I4_INIT_TYPE_DLLS,
I4_INIT_TYPE_BEFORE_OTHER,
I4_INIT_TYPE_OTHER,
I4_INIT_TYPE_AFTER_ALL //For anything that relies upon others
};
class i4_init_class
{
public:
static i4_init_class *first_init;
i4_init_class *next_init;
virtual int init_type() { return I4_INIT_TYPE_OTHER; }
virtual void init() {}
virtual void uninit() {}
i4_init_class();
virtual ~i4_init_class();
};
(the init_type is just an enumeration of initialisation priorities)Derived classes override the init() or uninit() method (or both), but contain no code (except variable initialisations) in their constructor. The actual code (like memory allocation/deallocation) has been moved to init()/uninit()
CPP file:
void i4_init()
{
I4_ASSERT(!i4_inited, "i4 already initialized");
//This is one of the few places where code is not time-critical
for (int t=0; t<=I4_INIT_TYPE_AFTER_ALL; t++)
{
i4_init_class *i=i4_init_class::first_init;
Current_Init_Class=0;
for (;i;i=i->next_init)
{
if (t==I4_INIT_TYPE_MEMORY_MANAGER)
{
Num_Init_Classes++;
}
Current_Init_Class++;
if (i->init_type()==t)//sucht alle init-Typen ab und initialisiert den Richtigen (Reihenfolge)
i->init();
}
if (t==I4_INIT_TYPE_MEMORY_MANAGER)
i4_inited=i4_T; // ok to allocate memory after this stage
}
i4_warning("Successfully initialized %d system components.",Num_Init_Classes);
}
void i4_uninit()
{
I4_ASSERT(i4_inited, "i4_uninit() without i4_init()");
for (int t=I4_INIT_TYPE_AFTER_ALL; t>=0; t--)
{
i4_init_class *i=i4_init_class::first_init;
for (;i;i=i->next_init)
if (i->init_type()==t)
i->uninit();
}
i4_inited=i4_F;
}
i4_init_class::~i4_init_class()
{
i4_init_class *last=0, *i=first_init;
for (;i && i!=this;)
{
last=i;
i=i->next_init;
}
if (!i)
i4_error("couldn't find init to remove");
if (last)
last->next_init=next_init;
else
first_init=next_init;
next_init=0;
}
i4_init_class::i4_init_class()
{
this->next_init=first_init;
first_init=this;
}
At the beginning of the prg, i call the static i4_init() function, at the end, I call i4_uninit. This ensures all classes get initialized/deinitialized in a specific order. Since I use a lot more singletons than init_types, some of them still get called in a random order, but I can make sure that they don't depend on each other. As an example, I don't care in which order my INIT_TYPE_DLLS-type classes get called, but I care that they're called after the memory manager is initialized.
Destruction happens in reverse order, of course.
#4
Posted 09 February 2006 - 05:42 PM
That's great pater, thanks for posting it. :)
With so many post concerning the ambiguities of static/global initialization and destruction, an approach like yours could save a lot people of many hassles.
With so many post concerning the ambiguities of static/global initialization and destruction, an approach like yours could save a lot people of many hassles.
#5
Posted 09 February 2006 - 07:26 PM
Time for a new code gem?
reedbeta.com - developer blog, OpenGL demos, and other projects
#6
Posted 09 February 2006 - 08:51 PM
Actually you should try to avoid complex objets build statically... Why do you need globals ?
#7
Posted 09 February 2006 - 10:07 PM
@MJeannig: You have to initialize your interdependent classes somehow. What problem do you see with pater's method?
#8
Posted 09 February 2006 - 10:24 PM
It may seem that having a lot of static classes is bad design. In many cases, I completelly agree to this, but the advantage of the method I'm using is, that it adding an additional class can be done without modification of other code. Some of my classes will add themselves to other lists (besides the one used by the i4_init_class itself), like the list of game objects, or the list of user commands. So adding i.e. a new game object type is done by just declaring a static instance of such a class, which then knows how this new object is going to be constructed. With some macro tricks, this is only a single line of code, at the very location where the object itself is declared. I don't need to have a file where all my objects are listed, and which includes every object's .h file to be able to call the object's constructor.
1 user(s) are reading this topic
0 members, 1 guests, 0 anonymous users











