Topic: static objects & construction order
Author: David Apfelbaum <da0g+@andrew.cmu.edu>
Date: Fri, 24 Mar 1995 23:57:04 -0500 Raw View
Excerpts from netnews.comp.std.c++: 16-Mar-95 static objects &
constructi.. by Donald Way@epix.net
> // a.hpp
> class a {
> int i;
> };
> // b.hpp
> class a;
> class b {
> static a foobar;
> };
> // c.hpp
> class b;
> class c {
> static b foobar;
> };
The fundamental problem is that the linker will resolve these static
objects in what effectively amounts to a random order. (Based somehow
upon the order of linking, I believe.) So it becomes very difficult to
assure that b::foobar gets constructed before c::foobar.
But am I missing something here? Couldn't you just do:
class A {
int i;
};
class B {
static A *objA
static void Initialize { if ( objA == (A *)NULL ) objA = new A; }
public:
B ( __whatever__ ) { B:Initialize(); __whatever__ }
};
class C {
static B *objB;
static void Initialize { if ( objB == (B *)NULL ) objB = new B; }
public:
C ( __whatever__ ) { C::Initialize(); __whatever__ }
};
Perhaps not as neat. But it does guarantee the order of construction.
And you could always define a public method inside, say, class C like:
static B & objB() { C::Initialize(); return * objB; }
for convience, if you so wished....
However, there can be a more significant problem, which is worthy of the
attention of comp.std.c++.
If you have, say, a base class that will contain a list of subclasses.
And you wish to have exactly one instance of each of those subclasses --
for example, with each representing a dictionary entry... You can wind
up with something like this:
//File A.H
class A {
static tableOfA *dictionaryEntries;
public:
static void Initialize () { if (dictionaryEntries == (tableOfA *)NULL)
dictionaryEntries = new table; }
}
//File A.C
tableOfA A::dictionaryEntries = (tableOfA *)NULL;
//File B.H
#include <A.H>
class B : public A {
static B RegisterInTable;
instance_varaibles;
public:
B ( void ) { A::Initialize();
A::dictionaryEntries->Add(this);
__whatever__
}
__whatever__
};
//File B.C
B B::RegisterInTable;
//File C.H
#include <A.H>
class C : public A {
static C RegisterInTable;
instance_varaibles;
public:
C ( void ) { A::Initialize();
A::dictionaryEntries->Add(this);
__whatever__
}
__whatever__
};
//File C.C
C C::RegisterInTable;
The problem that arises now is that if B::RegisterInTable and
C::RegisterInTable are the only instances of classes B & C, then there
is no guarantee that they will ever be contructed...
Specifically, on a Alpha box running OSF, the linker fails to include
these static definitions (from a non-shared library) in the final
program. (I am somewhat confident that it's the linker, since adding
the linker flags "-all" and "-none" around the library in question fixes
the problem, at the cost of including all of the code in that library in
the executable... *sigh*)
Perhaps there *should* be a hard and fast rule that ALL static objects
are automatically included and constructed before main() to specifically
deal with the case of side effects inside the constructor...
Just my $.02...
-David Apfelbaum.
Author: "Ronald F. Guilmette" <rfg@rahul.net>
Date: 18 Mar 1995 10:26:19 GMT Raw View
In article <donald-1603951821440001@qrvlppp10.epix.net>,
Donald Way <donald@epix.net> wrote:
>
>So, in conclusion, I ask you all: am I wrong to expect this? Does the
>Hiesenberg Uncertainty Principle really have a place in C++?...
Oh yes! Quite definitely!
There are whole huge clumps of different (small or large) things that
you might write in a C++ program that will result in ``undefined behavior''.
This is true in ISO standard C also, and in many other languages.
The difference is that in C++, the set of conditions which might
lead a program (or a translation unit) to exhibit (or to cause)
undefined behavior is not currently well documented. :-(
--
-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- E-mail: rfg@segfault.us.com ----------- Purveyors of Compiler Test ----
-------------------------------------------- Suites and Bullet-Proof Shoes -
Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: Sun, 19 Mar 1995 18:43:43 GMT Raw View
In article <3kecgb$fnp@hustle.rahul.net>,
Ronald F. Guilmette <rfg@rahul.net> wrote:
>There are whole huge clumps of different (small or large) things that
>you might write in a C++ program that will result in ``undefined behavior''.
>
>This is true in ISO standard C also, and in many other languages.
>
>The difference is that in C++, the set of conditions which might
>lead a program (or a translation unit) to exhibit (or to cause)
>undefined behavior is not currently well documented. :-(
While it would be nice to document many of these
things, it isn't actually necessary -- and probably not possible.
That is, any statement that certain behaviour is undefined
in the Standard is non-normative unless it expresses an exception
to something which would be otherwise well defined.
Basically, _everything_ is undefined unless explicitly
stated as being, or deducible as, well defined. Note in particular
that the behaviour of the translator presented with a syntax
error is well defined -- a diagnostic error message must be issued.
--
JOHN (MAX) SKALLER, INTERNET:maxtal@suphys.physics.su.oz.au
Maxtal Pty Ltd,
81A Glebe Point Rd, GLEBE Mem: SA IT/9/22,SC22/WG21
NSW 2037, AUSTRALIA Phone: 61-2-566-2189
Author: donald@epix.net (Donald Way)
Date: Sun, 19 Mar 1995 15:28:43 -0500 Raw View
In article <D5pAow.F71@ucc.su.OZ.AU>, maxtal@Physics.usyd.edu.au (John Max
Skaller) wrote:
> While it would be nice to document many of these
> things, it isn't actually necessary -- and probably not possible.
> That is, any statement that certain behaviour is undefined
> in the Standard is non-normative unless it expresses an exception
> to something which would be otherwise well defined.
>
> Basically, _everything_ is undefined unless explicitly
> stated as being, or deducible as, well defined. Note in particular
> that the behaviour of the translator presented with a syntax
> error is well defined -- a diagnostic error message must be issued.
Doesn't this passage from 3.4 explicitly state a behavior?
"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..."
I will acknowledge that implementing 3.4 is very problematic, but clearly
it is desirable to provide for some means by which an order can be imposed
on the execution of static constructors, and the intent of 3.4 seems to
recognize that.
To throw the initialization problem onto the heap of undefined behavior
and in doing so ignoring the problem altogether does not sit well with me,
particularly when compromise solutions appear possible.
--
Donald Way
donald@epix.net
Author: donald@epix.net (Donald Way)
Date: Thu, 16 Mar 1995 18:21:44 -0500 Raw View
I'm somewhat new to this forum, so please forgive me if this is old news.
I have a project which uses something similar to the exemplar idiom that
Coplien talks about in his book "Advanced C++: Programming Styles and
Idioms." Essentially this amounts to declaring a static member for a
class whose purpose is to collect and/or manage information relating to
all objects declared for that class. The definition of this member's
class itself includes a static member serving much the same purpose for
objects of its class. In code, it looks something like...
// a.hpp
class a {
int i;
};
// b.hpp
class a;
class b {
static a foobar;
};
// c.hpp
class b;
class c {
static b foobar;
};
In this project, before an object of type c is created, c::foobar has to
be constructed. Before an object of type b is constructed, b::foobar has
to be constructed. Unfortunately, the compiler I am using will not do
this for me. It will call the constructors for static objects in what
amounts to be a random order. Worse, the order in which the constructors
are called will change depending upon the context in which the program is
run (i.e., one way when run under the environment's debugger, another way
when run as a standalone binary).
I have now just concluded my third debate with the vendor over whether
this is a bug in their implementation. Each time we have this debate, we
each more or less agree to conclude that the other doesn't know what
they're talking about. :) As I have not written a C++ compiler myself, I
have to admit that it is entirely possible I'm missing some vital insight
into why their compiler emits the code it does, so I'd like to hear what
everyone else has to say about this.
We both agree that the relevant passage in the ARM is from section 3.4,
page 19, which states:
"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..."
I read this to mean that before an object may be constructed, any static
members contained within its class must have already been constructed
provided that the constructor for that class and the declaration for the
static member share the same translation unit. They maintain that the
passage is poorly worded -- particularly the part referring to "first use"
of an "object" -- and insist that this does not include the case of a
static object's construction.
I acknowledge that it is easy for the programmer to create an
unsupportable situation wherein constructors from two different classes
are mutually dependent (and the ARM cautions against this later in 3.4).
I am not doing this. I further acknowledge that, in the trivial case
outlined above, it is a simple matter to control the order of
instantiation by explicitly creating the objects from main() (for
example), or by placing the declarations for the static members within the
same translation unit. However I feel I have justifiable reasons against
taking this approach. To begin with, I have a great many classes which
enjoy the functionality afforded through the use of this idiom. Moreover,
these classes constitute a framework of sorts for several different
projects wherein each project makes use of only a subset of all these
classes. It is simply impractical and error prone to resort to any
explicit ordering of constructors given the frequent changes and additions
that occur to the base code. To say nothing of its being ugly.
The development system I am using generates a symbol table and a list of
addresses for the constructors to be called at program start. I *could*
use this information to write a simple program that would accept something
akin to a makefile in which the dependencies between translation units
could be entered and which would then reorder the constructors, but
unfortunately this appears viable only for standalone binaries... making
it work for the environment's debugger requires playing the role of the
caffiene-dependent system programmer and I'm getting too old for that kind
of nonsense. Furthermore, portability is something I rather tend to be in
favor of. Nevertheless, the fact that I can externally impose the correct
order for constructors leads me to believe that correctly (IMHO)
implementing this behavior needn't be a great big scient project on the
part of the vendor.
So, in conclusion, I ask you all: am I wrong to expect this? Does the
Hiesenberg Uncertainty Principle really have a place in C++? Is this a
problem that is being addressed by the powers that be? If so, will I like
their solution? If not, why not? Are there any compilers out there that
do what I want?
And in anticipation of bad news -- in the event there really is some
compelling reason for a compiler to beg off the question of ordering
static constructors -- what would you all think of a standard wherein
explicit ordering of constructors might be achieved through use of
something like a makefile as mentioned before?
Thank you for giving this matter your consideration.
--
Donald Way
donald@epix.net