Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Initialisation of template class static members

13 views
Skip to first unread message

Gareth Stockwell

unread,
May 10, 2004, 6:09:54 PM5/10/04
to
Could someone please help me with initialisation of static members of
a templated class? Basically, I have a template (Selection), which
has a static std::string member (type_name_). I also have a
SelectionManager class, which needs to do some one-off initialisation.
This initialisation involves the type_name_ member of several
instantitations of Selection.

My question, then, is how to ensure that Selection<T>::type_name_ has
been initialised (for some given values of T) before SelectionManager
attempts to access it.

Here is a sketch of the code:

//------------------
// selection.h
//--------------

template<class T>
class Selection {

static const std::string type_name_;

public:
static Selection& instance() { static Selection s; return s; }
const char* type_name() const { return type_name_.c_str(); }
};


//-------------------
// selection.cpp
//-----------------

template<>
const std::string Selection<Atom>::type_name_ =
"Selection<Atom>";

template<>
const std::string Selection<Monomer>::type_name_ =
"Selection<Monomer>"


//---------------------------
// selection_manager.h
//----------------------

class SelectionManager {

static const bool initialised_;
};


//--------------------------
// selection_manager.cpp
//------------------------

bool init_selection_manager() {

const char* s1 = Selection<Atom>::instance().type_name();
const char* s2 = Selection<Monomer>::instance().type_name();
}

const bool SelectionManager::initialised_ = init_selection_manager();


My initial instinct is to try to move the initialisations of the
type_name_ variable into a function, so that I could call it from the
top of init_selection_manager() ... but I can't get it to compile.

Any advice gratefully received.,
Gareth

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Dave Moore

unread,
May 11, 2004, 6:18:08 AM5/11/04
to
gareth_s...@hotmail.com (Gareth Stockwell) wrote in message news:<172ce8bd.04051...@posting.google.com>...

> Could someone please help me with initialisation of static members of
> a templated class? Basically, I have a template (Selection), which
> has a static std::string member (type_name_). I also have a
> SelectionManager class, which needs to do some one-off initialisation.
> This initialisation involves the type_name_ member of several
> instantitations of Selection.
>
> My question, then, is how to ensure that Selection<T>::type_name_ has
> been initialised (for some given values of T) before SelectionManager
> attempts to access it.
>

A couple of things about static initialization (adapted from 3.6.2/3
of the ISO C++ Standard):

1) the order of initialization of static variables across translation
units (more or less the same as .cpp files) is undefined .. IOW the
compiler can choose to do it any order.

2) a static variable is guaranteed to be initialized before the use of
any function or object defined in the same translation unit

So, static member data of a class is *guaranteed* to be initialized
the first time an object of the class is constructed. Therefore, one
way to solve your problem is to declare dummy variable(s) of the
class(es) you need to have static members initialized for at the top
of your function.

Another way is to put the definitions of the static member
specializations in the appropriate header files, since those will be
automatically added (via #include statements) to the translation units
where they are used.

HTH, Dave Moore


[code snipped]

ka...@gabi-soft.fr

unread,
May 12, 2004, 5:59:09 AM5/12/04
to
dtm...@rijnh.nl (Dave Moore) wrote in message
news:<306d400f.04051...@posting.google.com>...

> gareth_s...@hotmail.com (Gareth Stockwell) wrote in message
> news:<172ce8bd.04051...@posting.google.com>...

> > Could someone please help me with initialisation of static members
> > of a templated class? Basically, I have a template (Selection),
> > which has a static std::string member (type_name_). I also have a
> > SelectionManager class, which needs to do some one-off
> > initialisation. This initialisation involves the type_name_ member
> > of several instantitations of Selection.

> > My question, then, is how to ensure that Selection<T>::type_name_
> > has been initialised (for some given values of T) before
> > SelectionManager attempts to access it.

> A couple of things about static initialization (adapted from 3.6.2/3
> of the ISO C++ Standard):

> 1) the order of initialization of static variables across translation
> units (more or less the same as .cpp files) is undefined .. IOW the
> compiler can choose to do it any order.

> 2) a static variable is guaranteed to be initialized before the use of
> any function or object defined in the same translation unit

I don't know where you get this. In §3.6.2/3, it says "If the
initialization is deferred to some point in time after the first
statement of main, it shall occur before the first use of any function
or object defined in the same translation unit as the object to be
initialized." That's a big if, given that all implementations I know
actually initialize all static objects before entering main.

> So, static member data of a class is *guaranteed* to be initialized
> the first time an object of the class is constructed.

Not at all.

And of course, templates introduce another level of indeterminism,
because you don't define the actual class (which is an instantiation of
the template), the compiler does. Ditto the static variable -- and it
is undefined in which module the compiler considers it defined.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Gareth Stockwell

unread,
May 12, 2004, 12:42:29 PM5/12/04
to
> 1) the order of initialization of static variables across translation
> units (more or less the same as .cpp files) is undefined .. IOW the
> compiler can choose to do it any order.

So, if I understand correctly, the standard *does* guarantee that the
initialisation order *within* a TU is the same as the order in which
the object definitions appear in that TU. If that is the case, then
simply moving the static member specialisations to the top of the .cpp
file in which I reference them (selection_manager.cpp in this example)
should ensure that they are intialised before the call to
init_selection_manager().

This behaviour is what I see on my system (gcc 3.3.3 under Linux), but
is it portable?

drkm

unread,
May 12, 2004, 2:02:46 PM5/12/04
to
dtm...@rijnh.nl (Dave Moore) writes:

> 2) a static variable is guaranteed to be initialized before the use of
> any function or object defined in the same translation unit

? What about :

int f() {
return 0 ;
}
struct A {
static int myInt ;
} ;
A::myInt = f() ;

--drkm

Bill Wade

unread,
May 12, 2004, 2:10:07 PM5/12/04
to
dtm...@rijnh.nl (Dave Moore) wrote in message news:<306d400f.04051...@posting.google.com>...

> 2) a static [namespace scope] variable is guaranteed to be initialized before the use of


> any function or object defined in the same translation unit

unless the "use" occurs before main() is entered.

David Baraff

unread,
May 12, 2004, 8:12:35 PM5/12/04
to
ka...@gabi-soft.fr wrote in message news:<d6652001.04051...@posting.google.com>...

> dtm...@rijnh.nl (Dave Moore) wrote in message
> news:<306d400f.04051...@posting.google.com>...
> > gareth_s...@hotmail.com (Gareth Stockwell) wrote in message
> > news:<172ce8bd.04051...@posting.google.com>...
>
> > > > 2) a static variable is guaranteed to be initialized before the use of
> > any function or object defined in the same translation unit
"The spirit is willing, but the implementation is weak."

Consider:
-----A.cpp--------------
extern int B();

int varA = B();

int A() {
return varA + 1;
}

-----B.cpp--------------
extern int A();

int varB = A();

int B() {
#if CIRCULAR
return varB + 1;
#else
return 7;
#endif
}
-----------------------

If CIRCULAR is defined, what should the compiler do?
If CIRCULAR is not defined, is the compiler smart enough to figure out
that it needs to initialize varA first?

Basically, " before the use of any function or object defined" is near
impossible to implement -- once you start initialising some variable
with dynamic initialization, you can easily jump into other
translations units whose static have *not* been initialized.

Bottom line: in any implementation I know of, before main(), you
cannot depend upon *any* global data that has dynamic initialization
actually being initialized before you try to use it. This means that
in a library, dynamic initialization of *any* global variable makes
the entire library unsafe to use before main.

On systems which agressively delay initialization to after main(), all
dynamic initialization of global data is suspect.

llewelly

unread,
May 12, 2004, 8:31:18 PM5/12/04
to
gareth_s...@hotmail.com (Gareth Stockwell) writes:

>> 1) the order of initialization of static variables across translation
>> units (more or less the same as .cpp files) is undefined .. IOW the
>> compiler can choose to do it any order.
>
> So, if I understand correctly, the standard *does* guarantee that the
> initialisation order *within* a TU is the same as the order in which
> the object definitions appear in that TU.

More or less. See 3.6.2/1 . The two exceptions are static variables
which do not require dynamic initialisation (those are staticly
initialized, which happens 'before' all dynamic initialization),
and static objects which have function scope, which are
initialized the first time control flow passes through the
function.

> If that is the case, then
> simply moving the static member specialisations to the top of the .cpp
> file in which I reference them (selection_manager.cpp in this example)
> should ensure that they are intialised before the call to
> init_selection_manager().
>
> This behaviour is what I see on my system (gcc 3.3.3 under Linux), but
> is it portable?

The standard says it is. :-)

Dave Moore

unread,
May 12, 2004, 8:36:47 PM5/12/04
to

Yes, you are correct ... I see now that I was making some unstated
assumptions about the OP's problems .. and perhaps even those are
incorrect. Basically I was assuming that the variables in question
(that are dependent on proper initialization of static members of
classes) were declared in main, or were local variables created after
main had been entered. So, I think my statement above is correct
under those circumstances, and is only incorrect when static variables
are required for initialization of other static variables across
translation units, and thus implicitly before main has been entered.
However I not see (or at least think I do) that in that case my answer
to the OP was not particularly useful.



> > So, static member data of a class is *guaranteed* to be initialized
> > the first time an object of the class is constructed.
>
> Not at all.

Would you agree with the above statement if I ammend it to read:

" So, static member data of a class is *guaranteed* to be initialized

the first time an object of the class is constructed, provided that
program flow has already entered main."

?? This is what I meant in my original post, but as you correctly
identified, I did not state that.

>
> And of course, templates introduce another level of indeterminism,
> because you don't define the actual class (which is an instantiation of
> the template), the compiler does. Ditto the static variable -- and it
> is undefined in which module the compiler considers it defined.

Point taken ... but here is another question. Say I have the
following template class and static member definition in a header
file:

// foo.h ---------------------------------
template <typename T>
class foo {
public:
static int *i;
// rest of foo
};

int foo<T>::i = new int(5);

This template is then instantiated for type double in two separate
translation units, which will ultimately be linked together. So ..

// bar1.cpp -------------------------------
foo<double> a;

// barnone.cpp ----------------------------
foo<double> a;

How does the compiler deal with the initialization of foo<double>::i
?? Clearly foo<double>::i can only exist "in one place" in a given
program or it would violate the ODR (I think ... it at the very least
cause a link error). I guess 3.6.2 says only that the initialization
will happen:

a) sometime/place before main is entered
OR
b) after main is entered but before any variables of type foo<double>
have been constructed.

I actually am no longer completely confident about option b above,
because I cannot find the relevant passage(s) in the Standard (or else
I am mis-interpreting what I read there). However, it seems that it
must be somehow guaranteed by the Standard in order to ensure
well-defined behavior of C++ programs using template classes with
static member data. Is this true?

Finally, in relation to the OP's question, assuming what I said above
is right, I guess the answer is that the only way he can be certain
that static member's have been initialized is to use them after
program flow has entered main. Even then, the program will only have
defined behavior if there are no interdependecies of static variables
across translation units.

So, reading back, I think I have basically said the same thing several
different ways ... but have I got it straight now? Or am I still
confused?

TIA, Dave Moore

llewelly

unread,
May 13, 2004, 6:57:46 AM5/13/04
to
llewelly <llewe...@xmission.dot.com> writes:

> gareth_s...@hotmail.com (Gareth Stockwell) writes:
>
>>> 1) the order of initialization of static variables across translation
>>> units (more or less the same as .cpp files) is undefined .. IOW the
>>> compiler can choose to do it any order.
>>
>> So, if I understand correctly, the standard *does* guarantee that the
>> initialisation order *within* a TU is the same as the order in which
>> the object definitions appear in that TU.
>
> More or less. See 3.6.2/1 . The two exceptions are static variables
> which do not require dynamic initialisation (those are staticly
> initialized, which happens 'before' all dynamic initialization),
> and static objects which have function scope, which are
> initialized the first time control flow passes through the
> function.

Sorry to reply to my own post, but I forgot to list the most
important caveat: while order is guaranteed, it is not guaranteed
that any dynamicly initialized variable is initialized before main.
[snip]

ka...@gabi-soft.fr

unread,
May 13, 2004, 7:25:47 PM5/13/04
to
d...@pixar.com (David Baraff) wrote in message
news:<37483324.04051...@posting.google.com>...

> -----B.cpp--------------
> extern int A();

> int varB = A();

Initialize both modules before entering main, so that it is off the
hook:-).

> If CIRCULAR is not defined, is the compiler smart enough to figure out
> that it needs to initialize varA first?

> Basically, " before the use of any function or object defined" is near
> impossible to implement -- once you start initialising some variable
> with dynamic initialization, you can easily jump into other
> translations units whose static have *not* been initialized.

> Bottom line: in any implementation I know of, before main(), you
> cannot depend upon *any* global data that has dynamic initialization
> actually being initialized before you try to use it.

Sure you can. That which was declared earlier in the same translation
unit. Also, you can depend on that declared later having been zero
initialized, and only zero initialized, which can be useful on rare
occasions as well.

Thus, if we merge your two files, with A.cpp coming before B.cpp, the
standard guarantees that varB has not been initialized (or rather, that
it has been zero initialized) when B() is called to initialize varA. So
we are guaranteed that after initialization, varA == 1 and varB == 2
(supposing CIRCULAR is defined).

> This means that in a library, dynamic initialization of *any* global
> variable makes the entire library unsafe to use before main.

I certainly hope not. Using a global variable which requires dynamic
initialization in the initialization of another global variable is
dangerous, unless you write your code in a way that it can detect
whether initialization has taken place, and respond to it. (Such
detection almost always depends on zero initialization.)

> On systems which agressively delay initialization to after main(), all
> dynamic initialization of global data is suspect.

But since none exist, we are safe:-).

Note that if your two files were part of a DLL, and you load that DLL
after entering main, the compiler IS required to do the impossible.
Except that any use of a DLL takes you out of the realm of the standard;
all systems I know initialize static variables in a DLL when the DLL is
loaded, in the same way they initialize static variables in the main
program; i.e. with no defined order between modules, but before any call
to a function in the library that does not come from the initialization
takes place.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

llewelly

unread,
May 14, 2004, 11:06:54 AM5/14/04
to
drkm <use...@fgeorges.org> writes:

> dtm...@rijnh.nl (Dave Moore) writes:
>
>> 2) a static variable is guaranteed to be initialized before the use of
>> any function or object defined in the same translation unit
>
> ? What about :
>
> int f() {
> return 0 ;
> }
> struct A {
> static int myInt ;
> } ;
> A::myInt = f() ;

I think (2) as cited by Dave only applies to variables with static
initialization; it doesn't apply to variables with dynamic
initialization. I think James explained this best.

David Baraff

unread,
May 14, 2004, 8:55:07 PM5/14/04
to
> > Consider:
> > -----A.cpp--------------
> > extern int B();
> >
> > int varA = B();
> >
> > int A() {
> > return varA + 1;
> > }
>
> > -----B.cpp--------------
> > extern int A();
>
> > int varB = A();
>
> > int B() {
> > #if CIRCULAR
> > return varB + 1;
> > #else
> > return 7;
> > #endif
> > }
> > -----------------------
>
> > If CIRCULAR is defined, what should the compiler do?
>
> Initialize both modules before entering main, so that it is off the
> hook:-).
>
> > If CIRCULAR is not defined, is the compiler smart enough to figure out
> > that it needs to initialize varA first?
> Sure you can. That which was declared earlier in the same translation
> unit. Also, you can depend on that declared later having been zero
> initialized, and only zero initialized, which can be useful on rare
> occasions as well.

You're missing the point -- the code is in two different translation
units.
How does the compiler/runtime figure out the only safe order to do the
initialization?

>
> Thus, if we merge your two files,

Like I said, I'm not interested in this case -- I'm worried about
dynamic initialization which causes a code jump out of the translation
unit. This is very common.

> > This means that in a library, dynamic initialization of *any* global
> > variable makes the entire library unsafe to use before main.
>
> I certainly hope not. Using a global variable which requires dynamic
> initialization in the initialization of another global variable is
> dangerous,

yes

unless you write your code in a way that it can detect
> whether initialization has taken place, and respond to it. (Such
> detection almost always depends on zero initialization.)

Yes. Which leads one immediately to using pointers (zero
initialization) and functions to get the values which do the
initialization the first time they are called. In essence: don't plop
down global objects which have constructors. (constant initialization
of a POD is not a constructor, as I am using the term)

>
> > On systems which agressively delay initialization to after main(), all
> > dynamic initialization of global data is suspect.
>
> But since none exist, we are safe:-).

Not true. Darwin (mac OSX) does this. In fact Darwin (at the present
time) will, while in the middle of running one function for
initializtion, switch to running *another* function, then come back
and complete the first. Darwin comes as close to actually
implementing the standard as anything I've seen. This, unfortunately,
has tragic consequences. [not finishing a function, which breaks
function call semantics, results in deadlocks very quickly.]

James Kanze

unread,
May 15, 2004, 3:21:53 PM5/15/04
to
d...@pixar.com (David Baraff) writes:

|> > > int varA = B();

|> > > -----B.cpp--------------
|> > > extern int A();

|> > > int varB = A();

I have the impression that something got cut somewhere here. The
original code was in two translation units. I was pointing out that as
as user, you DO have two guarantees that you may be able to use: order
is guaranteed within a single translation unit, and zero initialization
is guaranteed before dynamic initialization.

|> How does the compiler/runtime figure out the only safe order to do
|> the initialization?

The same way it does in Modula-2 or Modula-3? It's not a real problem,
IF the language and the tools are designed to treat it. C++ originally
tried to make use of C linkers, which made any solution impossible.

|> > Thus, if we merge your two files,

|> Like I said, I'm not interested in this case -- I'm worried about
|> dynamic initialization which causes a code jump out of the
|> translation unit. This is very common.

|> > > This means that in a library, dynamic initialization of *any*
|> > > global variable makes the entire library unsafe to use before
|> > > main.

|> > I certainly hope not. Using a global variable which requires
|> > dynamic initialization in the initialization of another global
|> > variable is dangerous,

|> yes

|> > unless you write your code in a way that it can detect whether
|> > initialization has taken place, and respond to it. (Such detection
|> > almost always depends on zero initialization.)

|> Yes. Which leads one immediately to using pointers (zero
|> initialization) and functions to get the values which do the
|> initialization the first time they are called.

That's the most frequent solution. It isn't the only one.

|> In essence: don't plop down global objects which have constructors.
|> (constant initialization of a POD is not a constructor, as I am
|> using the term)

|> > > On systems which agressively delay initialization to after
|> > > main(), all dynamic initialization of global data is suspect.

|> > But since none exist, we are safe:-).

|> Not true. Darwin (mac OSX) does this. In fact Darwin (at the present
|> time) will, while in the middle of running one function for
|> initializtion, switch to running *another* function, then come back
|> and complete the first.

Which is forbidden by the standard.

|> Darwin comes as close to actually implementing the standard as
|> anything I've seen. This, unfortunately, has tragic consequences.
|> [not finishing a function, which breaks function call semantics,
|> results in deadlocks very quickly.]

It also makes sequence points irrelevant. Which is why the standard
forbids it.

--
James Kanze


Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung

9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34

David Baraff

unread,
May 16, 2004, 7:41:57 PM5/16/04
to
James Kanze <ka...@gabi-soft.fr> wrote in message news:<86wu3ex...@lns-vlq-4-82-64-160-110.adsl.proxad.net>...

> d...@pixar.com (David Baraff) writes:
>
> |> > > Consider:
> |> > > -----A.cpp--------------
> |> > > extern int B();
>
> |> > > int varA = B();
>
> |> > > int A() {
> |> > > return varA + 1;
> |> > > }
>
> |> > > -----B.cpp--------------
> |> > > extern int A();
>
> |> > > int varB = A();
>
> |> > > int B() {
> |> > > #if CIRCULAR
> |> > > return varB + 1;
> |> > > #else
> |> > > return 7;
> |> > > #endif
> |> > > }
> |> > > -----------------------
>
Let me try again. The initialization of varA requires us to call B(),
which is in a different translation unit. So suppose that we start out
by deciding to initialize varA. Will varB be initialized at the time
we call B()?

Nope. And the standard says it has to be.

Likewise, if we choose the other way, and initialize varB, first, we
must call A() PRIOR to initializing varA, which the standard says we
shouldn't do.

Now, if CIRCULAR isn't defined, it turns out that in fact, B() is safe
to call (i.e. gives the correct result) without initializaing varB(),
so the right thing in this case is to run the initializer for varA,
and *then* the initializer for varB.

If CIRCULAR is defined, there is no right answer.

Am I misreading the standard somehow?

A classic problem is this:

----------error.cpp---------------

static string progName = "unknown";

void SetProgramName(string s) {
progName = s;
}

void ReportError(string msg) {
printf("%s: error: %s\n", progName.c_str(), s.c_str());
}

------------anotherModule.cpp-------------

int InitMe() {
ReportError("I'm not implemented yet\n");
return 0;
}

------------aThirdModule.cpp--------------
int Compute() {
InitMe();
// stuff.
return 42;
}

int dynamicInitVar =Compute();


-----------------------------------------

The above will easily crash your program. It is *quite* likely that
at the time ReportError() is run, that progName has not had its
constructor run. Even though the definition for progName occurs
*before* that of ReportError, I don't know of any system that will
manage to run the constructor before calling ReportError(), if the
intiialization path begins with aThirdModule.cpp. If all of these
modules are in the same library, there is *no* guarantee that your
program is going to work right.

I hope you're not saying that this ought to always work -- sometimes
you get lucky, sometimes you don't.

Furthermore, there is *no* way for ReportError to check and see if
progName has had its constructor run yet. The way I would implement
this is to replace progName with a string*, statically initialized to
NULL. That will be my sentinel for whether or not initiailziation has
occurred.

ka...@gabi-soft.fr

unread,
May 17, 2004, 3:10:24 PM5/17/04
to
d...@pixar.com (David Baraff) wrote in message
news:<37483324.04051...@posting.google.com>...
> James Kanze <ka...@gabi-soft.fr> wrote in message
> news:<86wu3ex...@lns-vlq-4-82-64-160-110.adsl.proxad.net>...
> > d...@pixar.com (David Baraff) writes:

> > |> > > Consider:
> > |> > > -----A.cpp--------------
> > |> > > extern int B();

> > |> > > int varA = B();

> > |> > > int A() {
> > |> > > return varA + 1;
> > |> > > }

> > |> > > -----B.cpp--------------
> > |> > > extern int A();

> > |> > > int varB = A();

> > |> > > int B() {
> > |> > > #if CIRCULAR
> > |> > > return varB + 1;
> > |> > > #else
> > |> > > return 7;
> > |> > > #endif
> > |> > > }
> > |> > > -----------------------

> Let me try again. The initialization of varA requires us to call B(),
> which is in a different translation unit. So suppose that we start out
> by deciding to initialize varA. Will varB be initialized at the time
> we call B()?

> Nope. And the standard says it has to be.

No it doesn't. The standard makes no guarantees about the order of
initialization if initialization occurs before main. Which is the case
in all compilers I've ever used.

> Likewise, if we choose the other way, and initialize varB, first, we
> must call A() PRIOR to initializing varA, which the standard says we
> shouldn't do.

See above. As long as the two objects are in different translation
units, the standard makes no guarantees concerning order of
initialization.

> Now, if CIRCULAR isn't defined, it turns out that in fact, B() is safe
> to call (i.e. gives the correct result) without initializaing varB(),
> so the right thing in this case is to run the initializer for varA,
> and *then* the initializer for varB.

> If CIRCULAR is defined, there is no right answer.

> Am I misreading the standard somehow?

Yes. The standard makes no guarantees.

> A classic problem is this:

> ----------error.cpp---------------

> static string progName = "unknown";

> void SetProgramName(string s) {
> progName = s;
> }

> void ReportError(string msg) {
> printf("%s: error: %s\n", progName.c_str(), s.c_str());
> }

> ------------anotherModule.cpp-------------

> int InitMe() {
> ReportError("I'm not implemented yet\n");
> return 0;
> }

> ------------aThirdModule.cpp--------------
> int Compute() {
> InitMe();
> // stuff.
> return 42;
> }

> int dynamicInitVar =Compute();

> -----------------------------------------

> The above will easily crash your program.

Or not. You've got a 50/50 chance of it working:-).

> It is *quite* likely that at the time ReportError() is run, that
> progName has not had its constructor run.

Only because of the Peter principle.

Actually, it is highly likely to work until the final link, when you go
into integration, you've tested everything, and you don't have any more
time left for bug fixes.

(Historically, a common algorithm seems to be to initialize the
translation units in the reverse order they were incorporated into the
executable during link. At least, this is what CFront did. It was an
undocumented feature, however, and never guaranteed.)

> Even though the definition for progName occurs *before* that of
> ReportError,

What does "before" mean in this case. They are in two separate
compilation units.

On some compilers, if you link:
CC aThirdModule.o anOtherModule.o error.o -o myprog
the code will work. Even on those compilers, however, this isn't
guaranteed; it just happens to work this way.

I think the reasoning was that cout and cerr are normally defined in a
system library, and system libraries get incorporated at the end, so
even if the user forgot the necessary steps to ensure initialization,
they would be available in constructors of static objects. I'm just
guessing about this, though.

> I don't know of any system that will manage to run the constructor
> before calling ReportError(), if the intiialization path begins with
> aThirdModule.cpp. If all of these modules are in the same library,
> there is *no* guarantee that your program is going to work right.

> I hope you're not saying that this ought to always work -- sometimes
> you get lucky, sometimes you don't.

I never said anything like that. Someone (maybe it was you) said or
implied that handling initialization dependancies was impossible. All I
did was mention that other languages did it. In such languages, a
circular dependancy, such as you have constructed, is an error, and
requires a diagnostic.

At least in the cases I know, the languages in question have a fairly
rigorous concept of modules, and the dependencies are managed at the
module level, and not at the object level. Which means that the
compiler may find false dependencies, and signal errors where a solution
exists.

> Furthermore, there is *no* way for ReportError to check and see if
> progName has had its constructor run yet.

It could add a second variable:

bool f() ;


static string progName = "unknown" ;

bool progNameInitialized = f() ;

bool
f()
{
return true ;
}

Since they are in the same compilation unit (and both are dynamic
initialization), you are guaranteed that progName is initialized before
progNameInitialized. And that progNameInitialized is zero initialized,
which means initialized to false in the case of a bool, before *any*
dynamic initialization starts.

It's ugly, but it works.

Alternatively, in this particular case, you can simply use char[] or
char const*. I'd guess that 90% of my utilisation of C style arrays is
because of order of initialization issues -- a C style array, unlike
string or vector, can be statically initialized, and it is guaranteed
that all static initialization has occured before any dynamic
initialization.

> The way I would implement this is to replace progName with a string*,
> statically initialized to NULL. That will be my sentinel for whether
> or not initiailziation has occurred.

That's one solution. It's not the only one.

--
James Kanze GABI Software

Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung

9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

ka...@gabi-soft.fr

unread,
May 17, 2004, 3:14:24 PM5/17/04
to
llewelly <llewe...@xmission.dot.com> wrote in message
news:<86brktf...@Zorthluthik.local.bar>...
> llewelly <llewe...@xmission.dot.com> writes:

> > gareth_s...@hotmail.com (Gareth Stockwell) writes:

> >>> 1) the order of initialization of static variables across translation
> >>> units (more or less the same as .cpp files) is undefined .. IOW the
> >>> compiler can choose to do it any order.

> >> So, if I understand correctly, the standard *does* guarantee that the
> >> initialisation order *within* a TU is the same as the order in which
> >> the object definitions appear in that TU.

> > More or less. See 3.6.2/1 . The two exceptions are static variables
> > which do not require dynamic initialisation (those are staticly
> > initialized, which happens 'before' all dynamic initialization),
> > and static objects which have function scope, which are
> > initialized the first time control flow passes through the
> > function.

> Sorry to reply to my own post, but I forgot to list the most
> important caveat: while order is guaranteed, it is not guaranteed
> that any dynamicly initialized variable is initialized before main.

Just a nit, but it is guaranteed that those in the same compilation unit
as main are initialized before calling main.

There's also the fact that the guarantees required by the standard if
initialization is deferred after main are impossible to meet.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

David Baraff

unread,
May 18, 2004, 5:57:44 PM5/18/04
to
>> Even though the definition for progName occurs *before* that of
>> ReportError,

>What does "before" mean in this case. They are in two separate
>compilation units.

No, re-read the email. progName was defined in the same translation
unit as ReportError().
So the standard says progName is supposed to have its initializer run
before anyone tries to call ReportError(). If you try to call
ReportError() before main(), progName may or may not have been
constructed. That is all I'm saying.

Because of this, global objects with constructors are dangerous,
unless you can guarantee that no one tries to access them until after
main is called.

ka...@gabi-soft.fr

unread,
May 19, 2004, 10:36:03 PM5/19/04
to
d...@pixar.com (David Baraff) wrote in message
news:<37483324.04051...@posting.google.com>...

> >> Even though the definition for progName occurs *before* that of
> >> ReportError,

> >What does "before" mean in this case. They are in two separate
> >compilation units.

> No, re-read the email. progName was defined in the same translation
> unit as ReportError().

There are still two separate compilation units.

> So the standard says progName is supposed to have its initializer run
> before anyone tries to call ReportError().

No, the standard doesn't say that. It says that progName is supposed to
have its initializer run EITHER before main is called, OR before the
first use of any other object or function in the translation unit. All
implementations I know of choose the first option, maybe because the
second option is mathematically impossible (in the case of cycles, for
example).

If you call ReportError() before main has been entered, say from the
constructor of another static object in another compilation unit, all
bets are off. You have no guarantees.

> If you try to call ReportError() before main(), progName may or may
> not have been constructed. That is all I'm saying.

And that is what the standard says.

> Because of this, global objects with constructors are dangerous,
> unless you can guarantee that no one tries to access them until after
> main is called.

There are enough exceptions to make them sometimes useful:

- Static initialization always takes place before any dynamic
initialization. If you can declare your objects so that they will
be statically initialized, then there is no problem.

In this case, for example, rather than using std::string, the
old-fashioned C solution is actually preferrable:

char const* progName = "unknown" ;

Similarly, zero initialization is guaranteed to occur before
anything else. I use this, for example, to write thread safe
singletons, when I am sure that threading won't be started until
main has been entered, e.g.:

Singleton* Singleton::theInstance = Singleton::instance() ;

Singleton*
Singleton::instance()
{
if ( theInstance == NULL ) {
theInstance = new Singleton ;
}
return theInstance ;
}

- Order of initialization is guaranteed within a single translation
unit. This can be used in specific cases to ensure that an object a
is initialized before an object b, for example.

- I have a number of objects which by definition can't be used before
they are constructed, since they aren't visible to the rest of the
program until they have registered themselves globally, which they
do in their constructor. There's never any problem declaring these
static; in practice, that is about the only reasonable use of them.

In such cases, the global registry may have an order of
initialization problem, and of course, trying to extract the object
from the global registry before it has been registered will result
in a (controlled) failure. Still, I've found this strategy to be
useful in a number of cases.

As an example, you might look at my CommandLine/Option classes at my
site. GB_CommandLine is a singleton, and Option's are normally
declared statically. And since you cannot do anything but enrol
options until the GB_CommandLine instance has been passed argv and
argc, there is no risk of a premature attempt to extract an option.
I've used similar solutions for e.g. commands which are to be parsed
from a file (whose name is an argument on the command line).

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

0 new messages