[comp.lang.c++] Initializing Global Objects

sdm@cs.brown.edu (Scott Meyers) (05/27/91)

Section 3.4 of the ARM (pp. 20-21 of the American version) describes the
standard method for ensuring that a global object X is initialized before
it is used: put a static object inside each translation unit that can use
X, and have the constructor for the static object's class initialize X.
This is the function of the object iostream_init in the AT&T iostream
library -- it is used to properly initialize the predefined streams cin,
cout, etc.

My question concerns the static class variable that is used to determine
whether X has already been initialized.  From the ARM:

    class nifty_counter {
      static count;
    public:
      nifty_counter()
      {
        if (count++ == 0)
          // initialize global objects (i.e., X)
      }
    };

We know that nifty_counter::count is preinitialized to 0 because it is
static, but the rules of the language also require that it be initialized
by the programmer somewhere.  My question is this:  to what value should it
be initialized?  

In the iostream library, the value used is 0, i.e., one of the translation
units contains the statement:

    int nifty_counter::count = 0;

Prior to this statement is the declaration of the static nifty_counter
object, so we know that the nifty_counter constructor will have been called
by the time that nifty_counter::count is explicitly initialized.  As a
result, at the time when nifty_counter::count is initialized, it's value
will no longer be 0, since it will have been incremented inside the
constructor.  If its value is reset to 0 (the explicit initialization
value), then if a different nifty_counter object is subsequently
initialized (possible, since it could be from a different translation
unit), won't X be initialized *again*?

In other words, shouldn't the proper initialization value of count be as
follows?

    int nifty_counter::count = nifty_counter::count;

I suspect I'm overlooking something here, because the folks working on the
iostream library have had a lot of time to get it right.  What am I
missing?

Thanks,

Scott

-------------------------------------------------------------------------------
What do you say to a convicted felon in Providence?  "Hello, Mr. Mayor."

horstman@mathcs.sjsu.edu (Cay Horstmann) (05/27/91)

In article <76813@brunix.UUCP> sdm@cs.brown.edu (Scott Meyers) writes:
>
>My question concerns the static class variable that is used to determine
>whether X has already been initialized.  From the ARM:
>
>    class nifty_counter {
>      static count;
>    public:
>      nifty_counter()
>      {
>        if (count++ == 0)
>          // initialize global objects (i.e., X)
>      }
>    };
>
>We know that nifty_counter::count is preinitialized to 0 because it is
>static, but the rules of the language also require that it be initialized
>by the programmer somewhere.  My question is this:  to what value should it
>be initialized?  

To zero:
	int nifty_counter::count = 0;
(The =0 is optional.)

>
>Prior to this statement is the declaration of the static nifty_counter
>object, so we know that the nifty_counter constructor will have been called
>by the time that nifty_counter::count is explicitly initialized.  As a
>result, at the time when nifty_counter::count is initialized, it's value
>will no longer be 0, since it will have been incremented inside the
>constructor.  If its value is reset to 0 (the explicit initialization
>value), then if a different nifty_counter object is subsequently
>initialized (possible, since it could be from a different translation
>unit), won't X be initialized *again*?
>

I think you have the wrong conception of the initialization of static
integers. They are just set to 0 in the load image, so it is guaranteed
that they are zero before any constructors of static objects are executed.
(That is why in C this stuff was never an issue--all initial values
of statics were precomputed by the loader.) There is no code explicitly 
setting nifty_counter::count to 0. 

Cay

steve@taumet.com (Stephen Clamage) (05/27/91)

horstman@mathcs.sjsu.edu (Cay Horstmann) writes:

>In article <76813@brunix.UUCP> sdm@cs.brown.edu (Scott Meyers) writes:

>>    class nifty_counter {
>>      static count;
>>    public:
>>      nifty_counter()
>>      {
>>        if (count++ == 0)
>>          // initialize global objects (i.e., X)
>>      }
>>    };

>	int nifty_counter::count = 0;

>>Prior to this statement is the declaration of the static nifty_counter
>>object, so we know that the nifty_counter constructor will have been called
>>by the time that nifty_counter::count is explicitly initialized...

>I think you have the wrong conception of the initialization of static
>integers. They are just set to 0 in the load image, so it is guaranteed
>that they are zero before any constructors of static objects are executed.

Cay's analysis reflects current implementations, but not anything in the
language definition.  The language definition makes no distinction between
the initialization of
	int i = 1;	// value can be part of load image
and
	extern int j, k;
	class C { C(int, int); ... };
	C c(j, k);	// value cannot be part of load image

The requirement is merely that both initializations be complete before
the first statement of main() is executed.

In an embedded system, for example, non-const variables cannot be
initialized in the "load image" (ROM) because they then could not be
changed.  All variables must be initialized at program startup, including
nifty_counter::count.

Thus, Scott's concern is not fanciful.  The ANSI C++ committee (X3J16) is
addressing the issue of static initialization, and in particular, how, if
at all, initialization order can be specified.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

wmm@world.std.com (William M Miller) (05/28/91)

sdm@cs.brown.edu (Scott Meyers) writes:
> We know that nifty_counter::count is preinitialized to 0 because it is
> static, but the rules of the language also require that it be initialized
> by the programmer somewhere.

Actually, that's not quite accurate; what is required is that it be
*defined* somewhere; the initialization is optional.

>                               My question is this:  to what value should it
> be initialized?
>
> In the iostream library, the value used is 0, i.e., one of the translation
> units contains the statement:
>
>     int nifty_counter::count = 0;
>
> Prior to this statement is the declaration of the static nifty_counter
> object, so we know that the nifty_counter constructor will have been called
> by the time that nifty_counter::count is explicitly initialized.  As a
> result, at the time when nifty_counter::count is initialized, it's value
> will no longer be 0, since it will have been incremented inside the
> constructor.  If its value is reset to 0 (the explicit initialization
> value), then if a different nifty_counter object is subsequently
> initialized (possible, since it could be from a different translation
> unit), won't X be initialized *again*?
>
> In other words, shouldn't the proper initialization value of count be as
> follows?
>
>     int nifty_counter::count = nifty_counter::count;

Well, that would work, but it's redundant; the best definition is:

        int nifty_counter::count;       // implicitly initialized to 0

As you point out, this default initialization to 0 occurs before any of the
runtime static initializations, normally at load time, so the serialization
is correctly maintained.

However, the question you bring up is an interesting one.  The order of
static initializations in different compilation units, except for the
default initialization to 0, is undefined.  Although most (all?) current
implementations treat

        int nifty_counter::count = 0;

as completely synonymous with the default initialization, i.e., done at load
time, there's nothing I can find in the current specification that requires
that; in other words, it would be perfectly legal for an implementation to
run the nifty_counter constructors in few compilation units, incrementing
nifty_counter::count from its default initialization value of 0, and then
run the *explicit* initialization of nifty_counter::count, resetting the
value to 0.

I've relayed this question to the X3J16 ANSI Standard C++ Committee for
discussion; I think it's possible that the current specification is due to
an oversight and that initializations like this are intended to be done
prior to runtime static initializations.  Until this question is resolved,
however, the safest thing to do is to write static member definitions
without initializers and rely on the implicit initialization to 0.

-- William M. Miller, Glockenspiel, Ltd.
   wmm@world.std.com

wmm@world.std.com (William M Miller) (05/28/91)

steve@taumet.com (Stephen Clamage) writes:
> Cay's analysis reflects current implementations, but not anything in the
> language definition.  The language definition makes no distinction between
> the initialization of
>         int i = 1;      // value can be part of load image
> and
>         extern int j, k;
>         class C { C(int, int); ... };
>         C c(j, k);      // value cannot be part of load image
>
> The requirement is merely that both initializations be complete before
> the first statement of main() is executed.

Actually, it's not even defined that closely; section 3.4 of E&S says, "The
initialization of nonlocal static objects in a translation unit is done
before the first use of any function or object defined in that translation
unit.  Such initializations may be done before the first statement of main()
or deferred to any point in time before the first use of a function or
object defined in that translation unit."

-- William M. Miller, Glockenspiel, Ltd.
   wmm@world.std.com

Stephen.Clamage@sunbrk.FidoNet.Org (Stephen Clamage) (05/28/91)

horstman@mathcs.sjsu.edu (Cay Horstmann) writes:

>In article <76813@brunix.UUCP> sdm@cs.brown.edu (Scott Meyers) writes:

>>    class nifty_counter {
>>      static count;
>>    public:
>>      nifty_counter()
>>      {
>>        if (count++ == 0)
>>          // initialize global objects (i.e., X)
>>      }
>>    };

>	int nifty_counter::count = 0;

>>Prior to this statement is the declaration of the static nifty_counter
>>object, so we know that the nifty_counter constructor will have been called
>>by the time that nifty_counter::count is explicitly initialized...

>I think you have the wrong conception of the initialization of static
>integers. They are just set to 0 in the load image, so it is guaranteed
>that they are zero before any constructors of static objects are executed.

Cay's analysis reflects current implementations, but not anything in the
language definition.  The language definition makes no distinction between
the initialization of
	int i = 1;	// value can be part of load image
and
	extern int j, k;
	class C { C(int, int); ... };
	C c(j, k);	// value cannot be part of load image

The requirement is merely that both initializations be complete before
the first statement of main() is executed.

In an embedded system, for example, non-const variables cannot be
initialized in the "load image" (ROM) because they then could not be
changed.  All variables must be initialized at program startup, including
nifty_counter::count.

Thus, Scott's concern is not fanciful.  The ANSI C++ committee (X3J16) is
addressing the issue of static initialization, and in particular, how, if
at all, initialization order can be specified.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

 * Origin: Seaeast - Fidonet<->Usenet Gateway - sunbrk (1:343/15.0)

William.M.Miller@sunbrk.FidoNet.Org (William M Miller) (05/28/91)

sdm@cs.brown.edu (Scott Meyers) writes:
> We know that nifty_counter::count is preinitialized to 0 because it is
> static, but the rules of the language also require that it be initialized
> by the programmer somewhere.

Actually, that's not quite accurate; what is required is that it be
*defined* somewhere; the initialization is optional.

>                               My question is this:  to what value should it
> be initialized?
>
> In the iostream library, the value used is 0, i.e., one of the translation
> units contains the statement:
>
>     int nifty_counter::count = 0;
>
> Prior to this statement is the declaration of the static nifty_counter
> object, so we know that the nifty_counter constructor will have been called
> by the time that nifty_counter::count is explicitly initialized.  As a
> result, at the time when nifty_counter::count is initialized, it's value
> will no longer be 0, since it will have been incremented inside the
> constructor.  If its value is reset to 0 (the explicit initialization
> value), then if a different nifty_counter object is subsequently
> initialized (possible, since it could be from a different translation
> unit), won't X be initialized *again*?
>
> In other words, shouldn't the proper initialization value of count be as
> follows?
>
>     int nifty_counter::count = nifty_counter::count;

Well, that would work, but it's redundant; the best definition is:

        int nifty_counter::count;       // implicitly initialized to 0

As you point out, this default initialization to 0 occurs before any of the
runtime static initializations, normally at load time, so the serialization
is correctly maintained.

However, the question you bring up is an interesting one.  The order of
static initializations in different compilation units, except for the
default initialization to 0, is undefined.  Although most (all?) current
implementations treat

        int nifty_counter::count = 0;

as completely synonymous with the default initialization, i.e., done at load
time, there's nothing I can find in the current specification that requires
that; in other words, it would be perfectly legal for an implementation to
run the nifty_counter constructors in few compilation units, incrementing
nifty_counter::count from its default initialization value of 0, and then
run the *explicit* initialization of nifty_counter::count, resetting the
value to 0.

I've relayed this question to the X3J16 ANSI Standard C++ Committee for
discussion; I think it's possible that the current specification is due to
an oversight and that initializations like this are intended to be done
prior to runtime static initializations.  Until this question is resolved,
however, the safest thing to do is to write static member definitions
without initializers and rely on the implicit initialization to 0.

-- William M. Miller, Glockenspiel, Ltd.
   wmm@world.std.com

 * Origin: Seaeast - Fidonet<->Usenet Gateway - sunbrk (1:343/15.0)

comeau@ditka.Chicago.COM (Greg Comeau) (05/28/91)

In article <76813@brunix.UUCP> sdm@cs.brown.edu (Scott Meyers) writes:
:Section 3.4 of the ARM (pp. 20-21 of the American version) describes the
:standard method for ensuring that a global object X is initialized before
:it is used: put a static object inside each translation unit that can use
:X, and have the constructor for the static object's class initialize X.
:..My question concerns the static class variable that is used to determine
:whether X has already been initialized.  From the ARM:
:    class nifty_counter { static count; ... };
:We know that nifty_counter::count is preinitialized to 0 because it is
:static, but the rules of the language also require that it be initialized
:by the programmer somewhere.  My question is this:  to what value should it
:be initialized?  

That is not quite accurate.  We are told that it must be defined.
Although that has an implicit initialization mandate, it's far from
saying that the programmer must explicitely do it.

Point in fact, is that defining it is the way you should do it (I'll
go so far as to say that although the Iostream_init... = 0; example you
raised will probably work almost all the time, it is not guaranteed from
what I can see).  So you raise a good point.

- Greg
-- 
	 Comeau Computing, 91-34 120th Street, Richmond Hill, NY, 11418
                          Producers of Comeau C++ 2.1
          Here:attmail.com!csanta!comeau / BIX:comeau / CIS:72331,3421
                     Voice:718-945-0009 / Fax:718-441-2310

comeau@ditka.Chicago.COM (Greg Comeau) (05/28/91)

In article <750@taumet.com> steve@taumet.com (Stephen Clamage) writes:
:Cay's analysis reflects current implementations, but not anything in the
:language definition.  The language definition makes no distinction between
:the initialization of
:	int i = 1;	// value can be part of load image
:and
:	extern int j, k;
:	class C { C(int, int); ... };
:	C c(j, k);	// value cannot be part of load image
:The requirement is merely that both initializations be complete before
:the first statement of main() is executed.

That also sorta reflects current implementation though as that is only 1/2 the
requirement accto ARM p19: "Such initializations may be done before the first
statement of main() or deferred to any point in time before the first use of a
function or object defined in that translation unit."

- Greg
-- 
	 Comeau Computing, 91-34 120th Street, Richmond Hill, NY, 11418
                          Producers of Comeau C++ 2.1
          Here:attmail.com!csanta!comeau / BIX:comeau / CIS:72331,3421
                     Voice:718-945-0009 / Fax:718-441-2310

Greg.Comeau@sunbrk.FidoNet.Org (Greg Comeau) (05/29/91)

Reply-To: comeau@csanta.attmail.com (Greg Comeau)

In article <76813@brunix.UUCP> sdm@cs.brown.edu (Scott Meyers) writes:
:Section 3.4 of the ARM (pp. 20-21 of the American version) describes the
:standard method for ensuring that a global object X is initialized before
:it is used: put a static object inside each translation unit that can use
:X, and have the constructor for the static object's class initialize X.
:..My question concerns the static class variable that is used to determine
:whether X has already been initialized.  From the ARM:
:    class nifty_counter { static count; ... };
:We know that nifty_counter::count is preinitialized to 0 because it is
:static, but the rules of the language also require that it be initialized
:by the programmer somewhere.  My question is this:  to what value should it
:be initialized?  

That is not quite accurate.  We are told that it must be defined.
Although that has an implicit initialization mandate, it's far from
saying that the programmer must explicitely do it.

Point in fact, is that defining it is the way you should do it (I'll
go so far as to say that although the Iostream_init... = 0; example you
raised will probably work almost all the time, it is not guaranteed from
what I can see).  So you raise a good point.

- Greg
-- 
	 Comeau Computing, 91-34 120th Street, Richmond Hill, NY, 11418
                          Producers of Comeau C++ 2.1
          Here:attmail.com!csanta!comeau / BIX:comeau / CIS:72331,3421
                     Voice:718-945-0009 / Fax:718-441-2310

 * Origin: Seaeast - Fidonet<->Usenet Gateway - sunbrk (1:343/15.0)

Greg.Comeau@sunbrk.FidoNet.Org (Greg Comeau) (05/29/91)

Reply-To: comeau@csanta.attmail.com (Greg Comeau)

In article <750@taumet.com> steve@taumet.com (Stephen Clamage) writes:
:Cay's analysis reflects current implementations, but not anything in the
:language definition.  The language definition makes no distinction between
:the initialization of
:	int i = 1;	// value can be part of load image
:and
:	extern int j, k;
:	class C { C(int, int); ... };
:	C c(j, k);	// value cannot be part of load image
:The requirement is merely that both initializations be complete before
:the first statement of main() is executed.

That also sorta reflects current implementation though as that is only 1/2 the
requirement accto ARM p19: "Such initializations may be done before the first
statement of main() or deferred to any point in time before the first use of a
function or object defined in that translation unit."

- Greg
-- 
	 Comeau Computing, 91-34 120th Street, Richmond Hill, NY, 11418
                          Producers of Comeau C++ 2.1
          Here:attmail.com!csanta!comeau / BIX:comeau / CIS:72331,3421
                     Voice:718-945-0009 / Fax:718-441-2310

 * Origin: Seaeast - Fidonet<->Usenet Gateway - sunbrk (1:343/15.0)