Topic: Global Init/Shutdown revisited


Author: "Eugene Radchenko" <eugene@qsar.chem.msu.su>
Date: 1995/11/27
Raw View
Hi! That's me again with some more thoughts on the hard life of the static
variables.
I was very glad to learn that C++ Committee has effectively preempted my
second proposal (about explicitly requiring single lifetime stack for all
static objects). BTW, I do not see THAT hard a problem if the local static
happens to be in constructor/desctructor of global static:

>From: kanze@gabi-soft.fr (J. Kanze)
>X-Original-Date: 13 Nov 1995 11:04:49 +0100
>
>In general, static objects will *not* work in functions called from the
>destructors of other static objects *unless* the same function was
>called during the constructor (or before).  I believe that this is
>implicit in the standard, in section 3.6.3, which states that static
>objects will be destructed in the reverse order of there construction.
>For a given static object, x, any local static objects in functions
>called by the destructor will have already been destructed unless the
>function was called (and the local static object constructed) *before*
>the construction of x.
>
>There are at least two open points in my mind:
>
>1. When should a local static object be destructed if the block
>containing it is first executed in the destructor of a static object?  A
>(too) literal interpretation of the current wording would suggest that
>it must be destructed *before* destructing the object which triggered
>its construction, since it was certainly constructed after that object.
Ok, let's say we have the code
    struct Y { /*...*/ };
    struct X {
      X() { static Y yc; do_something_with(yc); }
      ~X() { static Y yd; do_something_else_with(yd); }
    };
    X x;
So, execution enters the X::X() constructor body, yc's constructor gets
called and 'yc' is pushed on the stack, the rest of x's constructor
completes and 'x' is also pushed (lifetime starts at construction
completion). When it comes to shutting the program down, first 'x' is
popped and its destructor is called. This causes 'yd' to be constructed and
pushed. When X::~X exits, the 'yd' is popped and destroyed,
immediately followed by the 'yc'. This order can result in problems only if
yc (or yd) is still dependent on the enclosing object (x) even after
X::~X() completes [F.i., Y is a server, X is a client and X::~X forgets to
get disconnected from the server].

However, the discussion has somehow drifted to mostly discussion of
shutdown (almost ignoring the problem of cross-unit initialization).
Although I agree that now (with the proper handling of local statics) the
required functionality can be attained, I would still like to have the neat
and portable way to do things like initialization of cout.
Actually, the only comment on this was:

>From: rac@intrigue.com (Robert Coie)
>X-Original-Date: Thu, 09 Nov 1995 12:37:03 -0800
>: The major problem is to ensure the right order of initialization (and
>: the inverse right order of destruction ;) between the translation units.
>: I think it can be solved by requiring that this initialization
>: really happens 'just' before the objects/functions in it are first accessed
>: - which may even happen before main() - when exactly is unimportant.
>
>I don't see how this can be guaranteed.  The simplest pathological case I
>can think of (although trivial) is:
>
>struct A { A(); };
>A stA;
>A::A() {}
>
>I can't see how you can guarantee that the initialization of stA occurs
>before the call to the constructor used to initialize it.  The burden on
>insuring that static objects are properly constructed before they are
>referenced must fall, in the end, on the programmer, as far as I can see.

So, let's suppose the above code is the module M1 and there is a variable
  A anotherA;
in some unit M2 which happens to be initialized first. All module M1 data
and code is originally flagged, so calling A::A() results in a exception
(not in C++ sense - if platform supports it). Exception handler removes the
flag and calls M1's initialization routine (which successfully constructs
stA). Then exception handler exits, causing the call to A::A() to be
retried (successfully this time). If virtual memory is not available, this
example still works if each M1's function (including A::A()) contains
implicit additional check to the effect of
  if(!module_initialized(M1))
    initialize_M1();
This functional approach does not help for cross-module data references,
though (like
  int aa = stA.aa;
assuming there is the appropriate member in A).

        So, what do you think about this?                   Genie

--
--------------------------------------------------------------------
Eugene V. Radchenko           Graduate Student in Computer Chemistry
E-mail: eugene@qsar.chem.msu.su                Fax: +7-(095)939-0290
Ordinary mail:  Chair of Organic Chemistry, Department of Chemistry,
                      Moscow State University, 119899 Moscow, Russia
*****************  Disappearances are deceptive  *******************
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]