Topic: Implicit constructors and destructors
Author: thp@cs.ucr.edu (tom payne)
Date: 1995/04/04 Raw View
Andy Sawyer (andys@thone.demon.co.uk) wrote:
[stuff deleted]
: Thirdly, if you're still not happy with this, try the following:
: (From the January '95 working paper)
: 9.4 Member functions [class.mfct]
: [...]
: 8 A static local variable in a member function always refers to the
: same object, whether or not the member function is inline.
: [...]
[more stuff deleted]
I'm trying to figure out how the compiler is expected to implement
this standard within the normal paradigm of independent compilation,
namely that, while compiling a given source file, the compiler has no
knowledge of the existence or characteristics of any other modules,
beyond what is specified in the header files.
Say the compiler is compiling whatever.cc which #includes widgets.h
which contains:
class Widget {
public:
void foo() {static int x; /* ... */ }
}
Now how is the compiler supposed to know whether space will be set
aside for x within some other some other .o-file or whether provision
for space for x has to be made within whatever.o?
Is there some additional requirement for a definition of such static
locals elsewhere in the program?
Tom Payne
Author: pstemari@erinet.com (Paul J. Ste. Marie)
Date: 1995/04/04 Raw View
In article <3lrr1c$j58@galaxy.ucr.edu>, thp@cs.ucr.edu (tom payne)
wrote:
[snip]
:Andy Sawyer (andys@thone.demon.co.uk) wrote:
:: (From the January '95 working paper)
:: 9.4 Member functions [class.mfct]
[snip]
:: 8 A static local variable in a member function always refers
:: to the same object, whether or not the member function is
:: inline.
:
[snip]
:Say the compiler is compiling whatever.cc which #includes
:widgets.h which contains:
:
: class Widget {
: public:
: void foo() {static int x; /* ... */ }
: }
:
:Now how is the compiler supposed to know whether space will be set
:aside for x within some other some other .o-file or whether
:provision for space for x has to be made within whatever.o?
With the standard Unix linker? Good luck. Other linkers can
identify and merge duplicate chunks of object code (psect overlay
attribute under Vax/VMS, something similar with Borland's tlink).
:Is there some additional requirement for a definition of such
:static locals elsewhere in the program?
It doesn't sound that way, and it definitely should not be that
way.
--Paul J. Ste. Marie, pstemari@well.sf.ca.us, pstemari@erinet.com
The Financial Crimes Enforcement Network claims that they capture every
public posting that has their name ("FinCEN") in it. I wish them good hunting.
Author: rubenst%occs.nlm.nih.gov (Mike Rubenstein Phoenix Contract)
Date: 1995/04/05 Raw View
tom payne (thp@cs.ucr.edu) wrote:
> Andy Sawyer (andys@thone.demon.co.uk) wrote:
> [stuff deleted]
> : Thirdly, if you're still not happy with this, try the following:
> : (From the January '95 working paper)
> : 9.4 Member functions [class.mfct]
> : [...]
> : 8 A static local variable in a member function always refers to the
> : same object, whether or not the member function is inline.
> : [...]
> [more stuff deleted]
> I'm trying to figure out how the compiler is expected to implement
> this standard within the normal paradigm of independent compilation,
> namely that, while compiling a given source file, the compiler has no
> knowledge of the existence or characteristics of any other modules,
> beyond what is specified in the header files.
> Say the compiler is compiling whatever.cc which #includes widgets.h
> which contains:
>
> class Widget {
> public:
> void foo() {static int x; /* ... */ }
> }
> Now how is the compiler supposed to know whether space will be set
> aside for x within some other some other .o-file or whether provision
> for space for x has to be made within whatever.o?
> Is there some additional requirement for a definition of such static
> locals elsewhere in the program?
Many compilers can handle this with the same kind of thing that's used for
Fortran named common. If it cannot be gracefully implemented, the compiler
always has the option of not inlining the code.
--
Mike Rubenstein
Author: "Ronald F. Guilmette" <rfg@rahul.net>
Date: 1995/03/31 Raw View
In article <smeyersD688CH.Bu1@netcom.com>,
Scott Meyers <smeyers@netcom.com> wrote:
>In article <796261305snz@thone.demon.co.uk> andys@thone.demon.co.uk writes:
>| inline Foo &my_object()
>| { static Foo my_object_really; return my_object_really; }
>
>This is one of the worst things you can do. Because Foo is inline, it has
>internal linkage, and you'll get a different copy of the function in each
>translation unit. Within each such function will be a *different* Foo
>object. In general, never make functions containing statics inline. If
>you feel compelled to do it anyway, declare the inline function extern.
>This should make it behave the way you want to, but different compilers do
>different things with functions declared like that.
At some point in the not-too-distant future, we can hope that compilers
will all do the same thing in that case (given that it has now, finally,
been decided what the semantics of such cases should be).
--
-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- E-mail: rfg@segfault.us.com ----------- Purveyors of Compiler Test ----
-------------------------------------------- Suites and Bullet-Proof Shoes -
Author: andys@thone.demon.co.uk (Andy Sawyer)
Date: 1995/03/31 Raw View
In article <smeyersD688CH.Bu1@netcom.com>
smeyers@netcom.com "Scott Meyers" writes:
>
> In article <796261305snz@thone.demon.co.uk> andys@thone.demon.co.uk writes:
> | inline Foo &my_object()
> | { static Foo my_object_really; return my_object_really; }
>
> This is one of the worst things you can do. Because Foo is inline, it has
> internal linkage, and you'll get a different copy of the function in each
> translation unit. Within each such function will be a *different* Foo
> object. In general, never make functions containing statics inline. If
> you feel compelled to do it anyway, declare the inline function extern.
> This should make it behave the way you want to, but different compilers do
> different things with functions declared like that.
>
> Scott
Firstly, Foo isn't inline, it's the return typeof my_object(), which IS
inline :-)
Secondly, given the original context the problem you mention would not
arise. I suggested this as a mechanism for 'fine-tuning' the order of
initialisation of file-scope static objects. Given that, the my_object()
function would be included only within the translation unit where the
original object would have been.
Thirdly, if you're still not happy with this, try the following:
(From the January '95 working paper)
9.4 Member functions [class.mfct]
[...]
8 A static local variable in a member function always refers to the
same object, whether or not the member function is inline.
[...]
So let's try this - It's a little extreme, but....
class FooStuff {
public:
static Foo &my_object()
{ static Foo my_object_really; return my_object_really; }
// This is still an inline function, since the body is declared within
// the class.
// You may still end up with a different my_object_really in each
// translation-unit, but that means your compiler is broken.
};
inline Foo& my_object()
{ return FooStuff::my_object(); }
And finally, I'm pretty sure that there is a rule similar to the above which
covers non-member functions (but it is now 2AM, So I'm not going to wade
through the WP trying to find it:-)
Regards,
Andy
--
* Andy Sawyer ** e-mail:andys@thone.demon.co.uk ** Compu$erve:100432,1713 **
The opinions expressed above are my own, but you are granted the right to
use and freely distribute them. I accept no responsibility for any injury,
harm or damage arising from their use. -- The Management.
Author: jamshid@ses.com (Jamshid Afshar)
Date: 1995/04/01 Raw View
In article <3l55eg$sp8@news.erinet.com>,
Paul J. Ste. Marie <pstemari@erinet.com> wrote:
>Does the WP address initialization of *file-scope* *non-objects*,
>*initialized with constants*, say for example pointers?
I think Ron's giving you a hard time because these terms are used so
loosely by C++ programmers and have rather different official meanings
then you might expect. The ARM terminology (3.5 Storage Classes) is:
"Automatic" objects are location to each invocation of block
"Static" objects exist and retain their values throughout the
execution of the entire program
and (3.2 Scopes):
"Local:" A name declared in a block [...]
"Function:" Labels [... as in "goto" labels -- that's IT]
"File:" A name declared outside all blocks and classes [ie, globals]
"Class:" The name of a class member [...]
Now don't confuse this with linkage! (see 3.3 Program and Linkage).
Variables declared in file scope with the "static" keyword have
"internal" linkage -- they can only be seen within that translation
unit. Static class data members have external linkage.
I'm scared to see how much ANSI/ISO has changed the terminology or
definitions. Anyway, static data members have "class" scope but a
"static" storage, err, class. When people say "function scope" they
probably mean "local" scope. When people say a variable has only file
scope, they probably mean it has only internal linkage.
>class Foo {
> private:
> static Registry *registry_;
> public:
> static Registry *registry();
>};
>
>Registry *Foo::registry_ = 0;
So you want to know when non-local (meaning scope), static (meaning
storage) objects (note: even pointer variable is an "object" in ARM
terminology) get initialized? 3.2 says "The default initialization of
all static objects to zero is performed before any dynamic (that is,
run-time) initialization". You can safely depend on Foo::registry_
being equal to the null pointer before any of your code statements are
executed. But be careful because I believe as far as the ARM is
concerned any other initialization is dynamic:
// foo.cc
char a[] = "hello";
char* b = a; // not a load-time initialization
// bar.cc
extern char a[];
extern char* b;
struct X {
char* m;
X() {
m = b;
}
} global;
int main() {
cout << (void*)global.m << endl;
cout << (void*)b << endl;
}
Believe it or not (I think), this program may output something like:
0x0000
0x4120
instead of what you would probably expect:
0x4120
0x4120
But, I believe the code does not cause undefined behavior -- global.m
must have been initialized to either 0 or &a[0].
>Registry *Foo::registry() {
> if (!registry_)
> registry_ = new Registry;
> return *registry_;
> };
>
>If Foo::registry_ has a guarantee of initialization prior to the
>first call of Foo::registry() *in a static ctor*, then this
>provides a general workaround for the order of static
>initialization problem.
Yes, but you also have to take care of destruction in the general
case. Keep in mind that a class whose constructor uses a global
object will probably also use that global in its destructor (eg,
Registry may output a message to a global log file in its ctor and
dtor). You take care of destruction with a call to atexit():
// a private static member function
void Foo::destroy_registry() { delete registry_; }
Registry *Foo::registry() {
if (!registry_) {
registry_ = new Registry;
atexit( &Foo::registry );
}
return *registry_;
};
By calling atexit() *after* it calls Registry::Registry(), you are
sure that registry_ will not be destroyed until after similar "global"
objects which are used by Registry::Registry() are destroyed. It
would be a lot simpler to rely on C++ to handle all this stuff:
Registry& Foo::registry() {
static Registry registry_;
return registry_;
}
But, it's not clear whether a compiler is required to "order" the
destruction of such statics as I describe above (ie, they may call
their internal atexit() before calling the Registry ctor).
It would be great if ANSI/ISO does guarantee the simpler function
would work since this provides a very adequate solution (just add a
couple of parenthesis) to the chronic C++ problem of static objects
referring to other static objects in their constructors and
destructors. Is there a resolution on this?
Jamshid Afshar
jamshid@ses.com
Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: 1995/04/02 Raw View
In article <3l9luu$c7l@news.erinet.com>,
Paul J. Ste. Marie <pstemari@erinet.com> wrote:
>: (2) it would introduce link or compile time overhead.
>
>That's certainly NOT a reason. Everything in C++ introduces link
>and compile time overhead, and if that was the criteria we'd still
>be dealing with K&R C.
Look, you can't really expect a _committee_ to act
rationally. "It" doesn't. It doesn't need reasons, it
needs consensus.
The committee has made plenty of stupid mistakes.
I can list them for you. Only....you wont agree and will
list a whole lot of things _you_ think are stupid mistakes with
which _I_ won't agree.
That's only two of us, now think about 80 people trying
to agree and you'll begin to understand just how much of a
miracle it is the committee has managed as well as it has
in making major extensions to the language, while preserving
as much code as possible. (A lot of the "blame" goes to
Bjarne Stroustrup :-)
>: So the status quo is that order of dynamic initialisation
>:of non-local variables is well defined only within a single
>:translation unit.
>: This makes portable definition of constants of the type of
>:a class defined in a library which depend on other constants
>:defined in that library impossible.
>
>Arrgggh.
Look, I agree. I originally said Australia would
make solution to this problem a _requirement_ for a YES vote.
But, pushed on the matter by the _author_ of the fine
proposal, I had to concede that would not be entirely reasonable.
I wish the proposal had been adopted, and not doing
so leaves a big hole in C++.
But it is only one of many. Such a proposal _requires_
a consensus of both users and implementors. That consensus
did not exist. So we cannot Standardise it. Perhaps this
is for the best. Sometimes we have to accept limitations.
--
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: alindbac@sw.seisy.abb.se (Anders Lindback)
Date: 1995/03/30 Raw View
In article <D61wxn.Bz9@research.att.com>,
Andrew Koenig <ark@research.att.com> wrote:
>In article <3l1ve7$fmq@engnews1.eng.sun.com> clamage@Eng.Sun.COM (Steve Clamage) writes:
>
>> >I think the language needs a way to declare what other globals a global
>> >depends on:
>
>> > Foo foo : bar; // bar must be constructed before foo
>
>> Members of the C++ committee labored for years hoping to find a solution
>> that was portable, implementable, usable, and effective. Everyone agreed
>> that it would be VERY nice to have such a solution. No one succeed in
>> finding one**. A proposal like yours above was examined, but turned out
>> not to work well.
>
>Indeed, every suggestion I know about so far has run aground
>on the same example.
>
>Suppose you have a class Thing that has some kind of hidden
>static member that must be initialized before any Things are
>created. Suppose further that you have a template called
>Container such that using a class Container<T> creates a static
>object of class T.
>
>It is possible for Thing and Container to be defined by two different
>people with no knowledge of each other.
>
>Now suppose a third person comes along and creates an object of
>type Container<Thing>. This has implicitly created the requirement
>that class Thing must be initialized before Container<Thing>.
>The user of Container<Thing> doesn't know about that requirement
>because it is implicit in the hidden parts of Thing and Container.
>The authors of Thing and Container don't know of each others' existence.
>
>So for any such scheme to be useful, the compiler must be able to
>detect the dependency and do things in the right order.
>
>So far no one has figured out a workable way to do this. So the burden
>remains on the shoulders of the authors of Thing and Container.
>--
> --Andrew Koenig
> ark@research.att.com
Suppose that for a class there exist a list of all other classes that
is access in this class.
Suppose in the constructor for an class a call is made to a specific
function for the first instance of this class (use a counter to
make sure this happens).
Call this function the dependency function. With each dependency function
is the dependency counter that tells if the class has been initialized.
The first thing in the dependency function is to set the dependency counter
to -1.
In the dependency function code exist to call other dependency functions,
the functions for the classes that this class is using in it's member
functions. The other classes depenency functions in their turn call their
used classes dependency functions.
If at any time a class dependency function is called when the counter is
-1, it means that there exist an circular dependencies and thus the
initialization and the program will fail.
Now when all other classes is ensured to be initialized the class can
merrilly continue, pleasantly ensured that all other classes it uses is
correctly initialized. And set the counter to 1, indication that the class
has been properly initialized. And also initialises it's static variables.
If the dependency function is called and the counter is 1 it just simply
returns.
All static object on the global level will become the same as a class
intthe above example. They will have their own dependency functions.
The function will just call there own class dependency function and
the initializes the variable. Note that if the variable has
dependencies to other classes or variabloes it must also call
them before it initializes itself.
Also structs etc will have to have the same dependency function like the
the classes and static variables in the above example.
This will result in some extra in runtime:
Speed: An extra test and optional function call for all objects
constructors.
Memory: One counter per class.
Code: One dependency function per class.
It will be 'some work' to implement the dependency function in the
the compiler (and/or linker).
So what will happen with Thingy and Container then;
the Container<Thingy> will in it's constructor call it's own dependency
function which will call the static Thingys dependency function which
will call the class Thingy's dependency function.
Thus it will work correctly.
Or have I missed something ???
Anders
Author: thp@cs.ucr.edu (tom payne)
Date: 1995/03/30 Raw View
: Andrew Koenig <ark@research.att.com> wrote:
[stuff deleted]
: >Indeed, every suggestion I know about so far has run aground
: >on the same example.
: >
: >Suppose you have a class Thing that has some kind of hidden
: >static member that must be initialized before any Things are
: >created. Suppose further that you have a template called
: >Container such that using a class Container<T> creates a static
: >object of class T.
: >
: >It is possible for Thing and Container to be defined by two different
: >people with no knowledge of each other.
: >
: >Now suppose a third person comes along and creates an object of
: >type Container<Thing>. This has implicitly created the requirement
: >that class Thing must be initialized before Container<Thing>.
: >The user of Container<Thing> doesn't know about that requirement
: >because it is implicit in the hidden parts of Thing and Container.
: >The authors of Thing and Container don't know of each others' existence.
: >
: >So for any such scheme to be useful, the compiler must be able to
: >detect the dependency and do things in the right order.
: >
: >So far no one has figured out a workable way to do this. So the burden
: >remains on the shoulders of the authors of Thing and Container.
: >--
: > --Andrew Koenig
: > ark@research.att.com
In fact, the hidden static member of Thing must be initialized before
any non-local static instance of Thing, a fact that is hidden from the
user and, hence, must be handled by the compiler. Doesn't the standard
require this?
Tom Payne
Author: smeyers@netcom.com (Scott Meyers)
Date: Thu, 30 Mar 1995 00:07:28 GMT Raw View
In article <796261305snz@thone.demon.co.uk> andys@thone.demon.co.uk writes:
| inline Foo &my_object()
| { static Foo my_object_really; return my_object_really; }
This is one of the worst things you can do. Because Foo is inline, it has
internal linkage, and you'll get a different copy of the function in each
translation unit. Within each such function will be a *different* Foo
object. In general, never make functions containing statics inline. If
you feel compelled to do it anyway, declare the inline function extern.
This should make it behave the way you want to, but different compilers do
different things with functions declared like that.
Scott
Author: pstemari@erinet.com (Paul J. Ste. Marie)
Date: Tue, 28 Mar 95 18:53:43 GMT Raw View
In article <D648Kn.DIJ@ucc.su.OZ.AU>,
maxtal@Physics.usyd.edu.au (John Max Skaller) wrote:
: A complete solution was in fact devised the problem of
:global initialisation dependencies. It even worked for templates.
:Despite encouraging further work on it with an explicit vote
:of the committee, the proposal was dropped.
:
: There were two problems. (1) it was untried.
That's a reason.
: (2) it would introduce link or compile time overhead.
That's certainly NOT a reason. Everything in C++ introduces link
and compile time overhead, and if that was the criteria we'd still
be dealing with K&R C.
:
: So the status quo is that order of dynamic initialisation
:of non-local variables is well defined only within a single
:translation unit.
: This makes portable definition of constants of the type of
:a class defined in a library which depend on other constants
:defined in that library impossible.
Arrgggh.
--Paul J. Ste. Marie, pstemari@well.sf.ca.us, pstemari@erinet.com
The Financial Crimes Enforcement Network claims that they capture every
public posting that has their name ("FinCEN") in it. I wish them good hunting.
Author: swf@elsegundoca.ncr.com (Stan Friesen)
Date: Mon, 27 Mar 1995 16:09:16 GMT Raw View
In article <1995Mar24.154505.22241@siemens.co.at>, helli@siemens.co.at (Tschenett Helmuth) writes:
|>
|> SVR4 has in fact this capability of implicitly running code at startup
|> end ending of a shared library (.ini- and .fini-sections in ELF).
|> But if you try to use it for construction and destruction of global
|> C++ objects in a shared library containing C++ code (g++ does it
|> exactly in this way) be aware of the following:
|>
|> - the order of calling this sections (.ini and .fini) is the same at
|> startup and end of the shared library and not the reverse order as
|> requested in ARM.
Oops.
Well, this can be fixed by using the .ini-* sections to run the
constructors, and having them register the destructors with atexit().
|>
|> - At least on SVR4.0 - which is the source for our UNIX-System -
|> there where some troubles on running code in the .ini-section. At example
|> it weren't possible to call "isalnum" because the ctype array is
|> not initialized at that time. (This is done later but before calling main)
This is a bug in the implementation, I would say.
--
swf@elsegundoca.attgis.com sarima@netcom.com
The peace of God be with you.
Author: "Ronald F. Guilmette" <rfg@rahul.net>
Date: 24 Mar 1995 20:28:08 GMT Raw View
In article <3kmvra$n91@visedge.vedge.com>, Alan Rooks <alan@leek.com> wrote:
>Ronald F Guilmette <rfg@rahul.net> writes:
>>>>> All I was saying is that I believe the final standard will say that
>>>>> file-scope objects must all be fully constructed before `main' is
>>>>> entered...
>
>Jason Merrill <jason@cygnus.com> writes:
>>>> Uh...why?
>
>Ronald F Guilmette <rfg@rahul.net> writes:
>>> Well, mostly because the ARM used to say some such thing.
>>> Are you saying that this requirement has been removed from the drafts
>>> Jason?
>
>Jason Merrill <jason@cygnus.com> writes:
>>Yes.
>>
>> 3.6.2 Initialization of non-local objects [basic.start.init]
>>
>> [paragraph removed for brevity]
>
>As far as I *ever* knew (like 4 years ago) the ARM never said anything
>about nonlocal statics necessarily being initialized before main() is
>entered. The rule has always been that nonlocal statics will be initialized
>before any use of a function or object defined in the file in which
>the nonlocal static appears. In fact, the paragraph Jason quotes above
>is almost identical to the one from my copy of the ARM (Oct 92 reprinting --
>section 3.4, bottom of page 19), and I think previous reprintings, as well.
Yes. I stand corrected.
(In practice, early implementations _did_ always initialize file-scope
statics before main, but that seems to have just been an implementation-
specific attribute of the implementations in question... not mandated
by either the ARM or the draft standard.)
--
-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- E-mail: rfg@segfault.us.com ----------- Purveyors of Compiler Test ----
-------------------------------------------- Suites and Bullet-Proof Shoes -
Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 25 Mar 1995 20:45:59 GMT Raw View
fenster@ground.cs.columbia.edu (Sam Fenster) writes:
>donald@epix.net (Donald Way) writes:
>> We need the ability to impose some very simple ordering on constructors....
>I think the language needs a way to declare what other globals a global
>depends on:
> Foo foo : bar; // bar must be constructed before foo
> ...
>I think the language needs to provide a way of specifying dependencies, either
>between translation units or between statics. The linker could detect loops,
>and could find a good order if there were none.
Members of the C++ committee labored for years hoping to find a solution
that was portable, implementable, usable, and effective. Everyone agreed
that it would be VERY nice to have such a solution. No one succeed in
finding one**. A proposal like yours above was examined, but turned out
not to work well.
(** It would be more accurate to say that no one succeeded in convincing
a majority of the committee that any proposed solution was suitable.)
One's first thought is that it can't be very hard, usually because one
has in mind some simple examples. One must also consider complex
relationships among static data in real programs, and whether a
proposed solution deals with them adequately.
Example: a class has a private static table requiring initialization.
Users of the class should not have to know about that implementation
detail. That is, if you create a T, you don't want to have to say
that the T object depends on a private data member of T. Yet a file-scope
object of class T must not be initialized until after the private
static data member, because the constructor uses that data. If a
proposed scheme doesn't handle this case automatically and conveniently,
there seems little point in burdening the language with half a solution.
--
Steve Clamage, stephen.clamage@eng.sun.com
Author: David Apfelbaum <da0g+@andrew.cmu.edu>
Date: Sat, 25 Mar 1995 23:50:11 -0500 Raw View
Excerpts from netnews.comp.std.c++: 23-Mar-95 Re: Implicit constructors
a.. by Sam Fenster@ground.cs.co
> I think the language needs a way to declare what other globals a global
> depends on:
>
> Foo foo : bar; // bar must be constructed before foo
>
This could get nasty with respect to detecting circular dependencies in
the link phase. ie:
Foo foo : bar;
Bar bar : baz;
Baz baz : foo;
(Hmm, and I wonder if an algorithm to detect this would be NP-Complete?)
Alternatively, perhaps a simpler rule could be implementable. What if
there was a rule that all base-class-scoped static objects had to be
initialized before any derived-class-scoped static objects were
initialized? And possibly that all derived-class-scoped static objects
had to be initialized before any file-scoped objects were initialized....
Comments?
-David.
Author: ark@research.att.com (Andrew Koenig)
Date: Sun, 26 Mar 1995 14:15:23 GMT Raw View
In article <3l1ve7$fmq@engnews1.eng.sun.com> clamage@Eng.Sun.COM (Steve Clamage) writes:
> >I think the language needs a way to declare what other globals a global
> >depends on:
> > Foo foo : bar; // bar must be constructed before foo
> Members of the C++ committee labored for years hoping to find a solution
> that was portable, implementable, usable, and effective. Everyone agreed
> that it would be VERY nice to have such a solution. No one succeed in
> finding one**. A proposal like yours above was examined, but turned out
> not to work well.
Indeed, every suggestion I know about so far has run aground
on the same example.
Suppose you have a class Thing that has some kind of hidden
static member that must be initialized before any Things are
created. Suppose further that you have a template called
Container such that using a class Container<T> creates a static
object of class T.
It is possible for Thing and Container to be defined by two different
people with no knowledge of each other.
Now suppose a third person comes along and creates an object of
type Container<Thing>. This has implicitly created the requirement
that class Thing must be initialized before Container<Thing>.
The user of Container<Thing> doesn't know about that requirement
because it is implicit in the hidden parts of Thing and Container.
The authors of Thing and Container don't know of each others' existence.
So for any such scheme to be useful, the compiler must be able to
detect the dependency and do things in the right order.
So far no one has figured out a workable way to do this. So the burden
remains on the shoulders of the authors of Thing and Container.
--
--Andrew Koenig
ark@research.att.com
Author: andys@thone.demon.co.uk (Andy Sawyer)
Date: Thu, 23 Mar 1995 00:42:06 +0000 Raw View
In article <3kpobc$sjl@news.erinet.com>
pstemari@erinet.com "Paul J. Ste. Marie" writes:
[snip]
> Does the WP address initialization of non-file-scope objects, say
> for example pointers? If we can assume that the pointers get
> initialized first, then you can do something like:
>
> class Foo {
> private:
> static Registry *registry_;
> public:
> static Registry *registry();
> };
>
> Registry *Foo::registry_ = 0;
> Registry *Foo::registry() {
> if (!registry_)
> registry_ = new Registry;
> return registry_;
> }
>
This will work fine, since Foo::registry_ will be initialised BEFORE the
if(!registry_) gets evaluated. Where there is a problem is in something
like this:
// FILE FOO.H
class Foo {
static FooTable s_Foos;
public:
const char *whoami;
Foo( const char *pName )
: whoami( pName )
{
// Each Foo registers itself in the table when created
s_Foos.Add( this );
}
static Foo *GetFoo( int Index )
{ return s_Foos.Lookup( Index ); }
};
// FILE FOOMAIN.CPP
#include "foo.h"
static FooTable Foo::s_Foos;
int main( int argc, char *arv[] )
{
int i = 0;
Foo *pFoo;
for( i = 0 ; pFoo = Foo::GetFoo( i ) ; ++i )
{
cout << pFoo->whoami << endl;
}
return 0;
}
// FILE FOOBAR1.CPP
static Foo Foobar1( "I'm Foo1" );
// FILE FOOBAR2.CPP
static Foo Foobar2( "I'm Foo2" );
The problem here is that, depending on the order of initialisation of
non-local objects, main() will get to pocess none,one or two Foos. I have
code that does this (and has loads of very big comments mentioning this as
implementation defined behaviour!). The technique is used to add new commands
to a macro language. Want a new function? Write it, and link it. Done - no
need to modify and recompile any existing code. Where my problem is, is that
if the Foo neve registers itself, no code in it's source file will get
executed, so the Foo won't get created, so it won't register itself, so no
code in its file gets executed...etc.
I would prefer that it was gaurenteed that all non-local objects were
initialised before main, since if we need finer control we can use the
inline-function-and-static-object method.
Of course, this DOES give rise to problems with dynamically linked libraries,
so maybe we need a new term - the execution unit, a level 'higher' than the
translation unit? I dunno.
Regards,
Andy
--
* Andy Sawyer ** e-mail:andys@thone.demon.co.uk ** Compu$erve:100432,1713 **
The opinions expressed above are my own, but you are granted the right to
use and freely distribute them. I accept no responsibility for any injury,
harm or damage arising from their use. -- The Management.
Author: girod@dshp01.trs.ntc.nokia.com (Marc Girod)
Date: 23 Mar 1995 07:34:49 GMT Raw View
>>>>> "rfg" == Ronald F Guilmette <rfg@rahul.net> writes:
In article <3ked16$g16@hustle.rahul.net> "Ronald F. Guilmette" <rfg@rahul.net> writes:
rfg> In article <JASON.95Mar17104930@phydeaux.cygnus.com>,
rfg> Jason Merrill <jason@cygnus.com> wrote:
>>>>>>> Ronald F Guilmette <rfg@rahul.net> writes:
>>
>>> All I was saying is that I believe the final standard will say that
>>> file-scope objects must all be fully constructed before `main' is
>>> entered... and they must all be destructed _after_ a normal return
>>> from main.
>>
>> Uh...why?
rfg> Well, mostly because the ARM used to say some such thing.
Should I buy a new ARM? Mine said:
Section 3.4 p 19
The initialization of nonlocal static objects ( 3.5) 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.
--
+---------------------------------------------------------------------------+
| Marc Girod - Nokia Telecommunications Phone: +358-0-511 27703 |
| TL4E - P.O. Box 12 Fax: +358-0-511 27432 |
| SF-02611 Espoo 61 - Finland Internet: marc.girod@ntc.nokia.com |
| X.400: C=FI, A=Elisa, P=Nokia Telecom, SUR=Girod, GIV=Marc |
+---------------------------------------------------------------------------+
Author: fenster@ground.cs.columbia.edu (Sam Fenster)
Date: 23 Mar 1995 21:17:20 GMT Raw View
donald@epix.net (Donald Way) writes:
> We need the ability to impose some very simple ordering on constructors. I
> fully appreciate (now) the complexities in required the compiler/linker to
> do this, but I still wonder why it should be so difficult if the standard
> adopted a convention wherein these dependencies could be specified using a
> separate source file (much like a makefile already resolves source/header
> dependencies).
I think the language needs a way to declare what other globals a global
depends on:
Foo foo : bar; // bar must be constructed before foo
The link phase could then construct globals to satisfy this partial ordering.
Dynamically linked modules that were needed for this would be loaded then.
I worked on a system where globals in one library needed globals in an
unrelated library that it was using: Our global objects contained libg++
linked lists, which use a global Nil object that must be constructed.
My solution was to use a file that declared dependencies between object files.
In the link phase, the order in which object files were linked was determined
from this dependency file. The order for cfront was the opposite of the order
for g++, but at least both were well-defined. Then, we ported to Lucid, which
does dynamic linking, and found that there was no guaranteed order.
We rewrote our code, replacing
Foo my_object;
with
Foo &my_object () {static Foo *my_object_p = new Foo; return *my_object_p;}
but this requires all code to say "my_object()" instead of "my_object", and it
requires the compiler to generate a flag that's checked every time my_object()
is called. Thus, even though my_object() is inline, it's slower that just
referencing the address of a static.
I think the language needs to provide a way of specifying dependencies, either
between translation units or between statics. The linker could detect loops,
and could find a good order if there were none.
But if such a feature is never added, keep in mind the idiom above.
Author: pstemari@erinet.com (Paul J. Ste. Marie)
Date: Fri, 24 Mar 95 02:11:49 GMT Raw View
In article <JASON.95Mar18053414@phydeaux.cygnus.com>,
jason@cygnus.com (Jason Merrill) wrote:
: 3.6.2 Initialization of non-local objects [basic.start.init]
:
: +------- BEGIN BOX 17 -------+
: This is still under active discussion by the committee.
: +------- END BOX 17 -------+
:
:1 The initialization of nonlocal static objects (_basic.stc_)
: in a translation unit is done before the first use of any
^^^^^^^^^^^^^^^^^^^^^
: function or object defined in that translation unit. Such
^^^^^^^^^^^^^^^^^^
That's the traditional statement. Sounds good so far. The
statement:
SomeClass SomeOtherClass::StaticMember(ctor args);
is the definition of SomeOtherClass::StaticMember, correct?
Therefore, since SomeOtherClass::StaticMember is defined within
this translation unit, all static objects defined within this
translation unit, _including SomeOtherClass::StaticMember_, will be
initialized before the first use of SomeOtherClass::StaticMember,
_in any translation unit_.
: initializations (_dcl.init_, _class.static_, _class.ctor_,
: _class.expl.init_) can 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.
: The default initialization of all static objects to zero
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
: (_dcl.init_) is performed before any other initialization.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Well, that's good. This allows the workaround of replacing static
objects with static pointers and using the singleton pattern to
construct them on the heap upon demand.
: [...] No further order is imposed on the
: initialization of objects from different translation units.
Now these seem to be weasel words that contradict the first
statement. If a ctor of a non-local static object in another
translation unit "uses" a static object defined in this translation
unit, it would seem to follow that the ctors for this translation
unit must be performed before the ctors in that other translation
unit.
I would define "uses" here to mean "takes the address of, calls a
member function on, ....". Now, I admit that this is going to be a
real PITA to actually implement, and probably would require the
linker to operate at a function, not a translation unit, level.
But if the implementers cannot bite that bullet, the first sentence
should probably be changed to read:
1 The initialization of nonlocal static objects (_basic.stc_)
in a translation unit is done before the first use of any
function or object defined in that translation unit _after
the first entry to main_.
ie use of functions or objects inside of the ctors of non-local
static objects not defined within the translation unit results in
undefined behavior.
I don't LIKE this interpretation, but it does seem to be what is
really mean here.
Without this, you would need to have a digraph with the nodes
being functions/objects, and the edges being references. The linker
would have to take the digraph, trace out the portion accessable
from the nodes corresponding to the static objects, do a
topological sort, and use the results to determine the order of
initialization. If you tried to do this on a TU basis instead of
on a function basis you would undoubtedly wind up with intractable
cycles in the digraph.
--Paul J. Ste. Marie, pstemari@well.sf.ca.us, pstemari@erinet.com
The Financial Crimes Enforcement Network claims that they capture every
public posting that has their name ("FinCEN") in it. I wish them good hunting.
Author: helli@siemens.co.at (Tschenett Helmuth)
Date: Fri, 24 Mar 1995 15:45:05 GMT Raw View
In article <D5r9pA.Gp5@lcpd2.SanDiegoCA.ATTGIS.COM>, swf@elsegundoca.ncr.com (Stan Friesen) says:
>
>Well, the DLL's in SysVr4 have the capability of including a routine that
>is run automatically when the library is loaded. It would be quite efficient
>to make this init routine call the constructors for the file scope objects
>in the library, and add them to the list of objects to be destroyed on exit.
>Alternatively, the destructors could be called from the library's termination
>function. [These routines are specified in the DT_INIT and DT_FINI entries
>of the .dynamic section of a DLL according to the Sys. V Application Binary
>Interface].
>
>I really think it is VITAL that the C++ standard be written to allow this
>implementation.
>
SVR4 has in fact this capability of implicitly running code at startup
end ending of a shared library (.ini- and .fini-sections in ELF).
But if you try to use it for construction and destruction of global
C++ objects in a shared library containing C++ code (g++ does it
exactly in this way) be aware of the following:
- the order of calling this sections (.ini and .fini) is the same at
startup and end of the shared library and not the reverse order as
requested in ARM.
- At least on SVR4.0 - which is the source for our UNIX-System -
there where some troubles on running code in the .ini-section. At example
it wheren t possible to call "isalnum" because the ctype array is
not initialized at that time. (This is done later but before calling main)
We had much troubles in our project by using C++ with shared librarys
especially with global objects and their initialization.
My opinion is that the ARM should define more precisily what should
happen with global objects in C++. At the moment the few phrases about
this in the ARM aren t really satisfying for compiler writers.
But there is one solution to all the problems with global objects:
DON T USE GLOBAL OBJECTS
In most cases it is possible to replace it with static global objects
and access methods to this object. Global data in most of the cases is
only the practice of programmers which are new to OO (may coming from
a procedural world) and not really the need of the application to write.
-------------------------------------------------------------------------
helli@siemens.co.at Tel. +49/1/60171/5666
Author: jbuck@synopsys.com (Joe Buck)
Date: 24 Mar 1995 22:35:42 GMT Raw View
alan@leek.com (Alan Rooks) writes:
>As far as I *ever* knew (like 4 years ago) the ARM never said anything
>about nonlocal statics necessarily being initialized before main() is
>entered. The rule has always been that nonlocal statics will be initialized
>before any use of a function or object defined in the file in which
>the nonlocal static appears.
And the ARM rule is unimplementable. It is trivial to create two files
that both have nonlocal static class objects, such that the constructors
for these class objects call functions in the other file, which of course
creates deadlock.
>In fact, the paragraph Jason quotes above
>is almost identical to the one from my copy of the ARM (Oct 92 reprinting --
>section 3.4, bottom of page 19), and I think previous reprintings, as well.
The difference, I believe, is that it no longer makes as strong a promise
as is made in the ARM. Now we merely know that initialization will either
happen before main, or it can be deferred to a later time (before any
functions in the file are called). The circular dependency is gone. This
still leaves the order-of-constructors issue hanging, though; sometimes
you do have code that requires some assumption about the order in which
objects in different files get constructed, and presently you must kludge
around this.
--
-- Joe Buck <jbuck@synopsys.com> (not speaking for Synopsys, Inc)
Phone: +1 415 694 1729
Author: pstemari@erinet.com (Paul J. Ste. Marie)
Date: Sun, 26 Mar 95 18:17:40 GMT Raw View
In article <cjRD_3u00WA70u2XlJ@andrew.cmu.edu>,
David Apfelbaum <da0g+@andrew.cmu.edu> wrote:
:This could get nasty with respect to detecting circular
:dependencies in the link phase. ie:
:
:Foo foo : bar;
:Bar bar : baz;
:Baz baz : foo;
:
:(Hmm, and I wonder if an algorithm to detect this would be
:NP-Complete?)
It's actually linear given direct indexing into storage. Knuth
gives an example.
:Alternatively, perhaps a simpler rule could be implementable.
:What if there was a rule that all base-class-scoped static objects
:had to be initialized before any derived-class-scoped static
:objects were initialized? And possibly that all
:derived-class-scoped static objects had to be initialized before
:any file-scoped objects were initialized....
:
:Comments?
It's arbitary, simplistic, and I believe it solves every such
problem I've encountered. I like it.
--Paul J. Ste. Marie, pstemari@well.sf.ca.us, pstemari@erinet.com
The Financial Crimes Enforcement Network claims that they capture every
public posting that has their name ("FinCEN") in it. I wish them good hunting.
Author: fenster@ground.cs.columbia.edu (Sam Fenster)
Date: 26 Mar 1995 20:04:51 GMT Raw View
David Apfelbaum <da0g+@andrew.cmu.edu> writes:
> Sam Fenster <fenster@ground.cs.columbia.edu> writes:
> > I think the language needs a way to declare what other globals a global
> > depends on:
> >
> > Foo foo : bar; // bar must be constructed before foo
>
> This could get nasty with respect to detecting circular dependencies in
> the link phase....:
> (Hmm, and I wonder if an algorithm to detect this would be NP-Complete?)
Creating a total order from a partial order seems to be no problem for the
Unix `tsort' utility, or for the Unix `make' utility. They take a reasonable
time, and can detect loops.
> Alternatively, perhaps a simpler rule could be implementable. What if
> there was a rule that all base-class-scoped static objects had to be
> initialized before any derived-class-scoped static objects were
> initialized? And possibly that all derived-class-scoped static objects
> had to be initialized before any file-scoped objects were initialized....
How does this help me if a global Foo object has a List member, and List uses
a global Nil object?
Author: andys@thone.demon.co.uk (Andy Sawyer)
Date: Sun, 26 Mar 1995 23:41:45 +0000 Raw View
In article <FENSTER.95Mar23161721@ground.cs.columbia.edu>
fenster@ground.cs.columbia.edu "Sam Fenster" writes:
[snip]
>
> We rewrote our code, replacing
> Foo my_object;
> with
> Foo &my_object () {static Foo *my_object_p = new Foo; return *my_object_p;}
>
> but this requires all code to say "my_object()" instead of "my_object", and it
> requires the compiler to generate a flag that's checked every time my_object()
> is called. Thus, even though my_object() is inline, it's slower that just
> referencing the address of a static.
>
[snip]
Another problem with this implementation is that the destructor for this
object will never get called. A better mechanism would be:
inline Foo &my_object()
{ static Foo my_object_really; return my_object_really; }
The object will get constructed the first time the function my_object() is
called, and will be destructed after the exit of the main() function.
It does not, however, workaround any of your other (justifiable) comments
regarding this mechanism.
However, the very existance of this mechanism serve (IMHO) as a good
argument for all global objects to be fully initialised before the entry to
main(). Since the aforementioned mechanism allows us to defer 'global' object
construction until usage, we can (if required) emulate the current behaviour.
There is, however, no way to emulate the everything constructed before main
behaviour. As I understand it, the main objection to this is related to the
use of dynamically linked libraries.
Now consider the following case :
dynalib1.cc (This is a dynamically linked library!)
static Foo TheFoo;
void someFunc( void ) {}
mainprog.cc
class DynaLib {
public:
DynaLib( const char *theLib ) { /* Link to the library */ }
~DynaLib() { /* Unlink the Library */ }
CallIt() { /* Calls Linked Library 'someFunc'*/ }
};
void DoIt( const char *libname )
{
DynaLib Lib( libname );
Lib.CallIt();
}
int main( int argc, char *argv[] )
{
DoIt( "dynalib1" );
DoIt( "dynalib1" );
return 0;
}
Now, the dynamic library gets linked AND unlinked twice here. When does the
Foo object get destroyed? The language tells us that this will be after
main() returns....but the library has been unlinked by then. The current
definitions do not work for multiple executable units (and, IMHO, cannot be
made to work without adding even more cloud to the issue without defining
the term.).
I would like to see a mechanism similar to the current 'translation-unit'
based system, but applied at an execution unit level. I geuss the major
problem with this is expressing it clearly and precisely...For instance,
I know what I mean, but I don't think the above desctiption makes much sense.
Sill, I can feel safe in the knowledge that my 'execution-unit' suggestion
is only marginally more vague than the 'translation-uint' defintion.
Consider:
somefunc.cc
static Foo foo;
void someFunc() {}
some_code_in_shared_lib_with_somefunc_statically_linked.cc
void doodle {} ( someFunc() );
some_code_in_main_program_but_still_statically_linked_to_somefunc.cc
int main()
{
doodle(); // Need to dynamic link to lib.
someFunc(); // Call statically linked function
}
Now, how many foos do we get? Since theeis only one TRANSLATION unit
somefunc.cc, we should only get one foo? I'd like to see a compiler/
linker/os smart enough to spot this one...
Starting to waffle now. Time to go :-)
Regards,
Andy
--
* Andy Sawyer ** e-mail:andys@thone.demon.co.uk ** Compu$erve:100432,1713 **
The opinions expressed above are my own, but you are granted the right to
use and freely distribute them. I accept no responsibility for any injury,
harm or damage arising from their use. -- The Management.
Author: "Ronald F. Guilmette" <rfg@rahul.net>
Date: 26 Mar 1995 21:39:50 GMT Raw View
In article <3kpobc$sjl@news.erinet.com>,
Paul J. Ste. Marie <pstemari@erinet.com> wrote:
>
>Does the WP address initialization of non-file-scope objects, say
>for example pointers?...
>...
>class Foo {
> private:
> static Registry *registry_;
> public:
> static Registry *registry();
>};
You have not shown this class definition nested within any other scope,
so we must assume that you meant to imply that this class type definition
itself occurs at file scope.
Given that, please note that the declaration of the `registry_' static
data member is _only_ a declaration. It must be accompanied by a
*definition* someplace else, and that *definition* will itself be
treated as a file-scope object definition.
In other words, to use your example to ask a question about the
initialization of non-file-scope objects is nonsensical, because
your example neither shows nor implies any such objects.
Perhaps you had another example in mind(?)
--
-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- E-mail: rfg@segfault.us.com ----------- Purveyors of Compiler Test ----
-------------------------------------------- Suites and Bullet-Proof Shoes -
Author: fenster@ground.cs.columbia.edu (Sam Fenster)
Date: 26 Mar 1995 21:34:32 GMT Raw View
>>donald@epix.net (Donald Way) writes:
>>> We need the ability to impose some very simple ordering on constructors....
> fenster@ground.cs.columbia.edu (Sam Fenster) writes:
>>I think the language needs a way to declare what other globals a global
>>depends on:
>>
>> Foo foo : bar; // bar must be constructed before foo
>>
>> ...
>>I think the language needs to provide a way of specifying dependencies,
>>either between translation units or between statics. The linker could
>>detect loops, and could find a good order if there were none.
clamage@Eng.Sun.COM (Steve Clamage) writes:
> Members of the C++ committee labored for years hoping to find a solution....
Well, if it's been decided, it's been decided, but it's still educational to
discuss it:
> One's first thought is that it can't be very hard....
Au contraire: It seems that there is no way a compiler could automatically
determine dependencies among globals. That's why I think the implementor must
declare them explicitly. Once that's done, it indeed doesn't seem very hard.
In some cases, the appropriate place to declare such a dependency would be in
the definition of a constructor, rather than in the definition of a global
object:
struct Foo {Bar b; Foo() needs global_b {b = global_b;}};
Foo f;
But not always:
struct Goo {Goo (Bar b);};
Goo g (global_b) needs global_b;
> Example: a class has a private static table requiring initialization. Users
> of the class should not have to know about that implementation detail....Yet
> a file-scope object of class T must not be initialized until after the
> private static data member, because the constructor uses that data.
I think the solution here must be to declare the table's dependency on the
*class*, rather than on the private static member of the class:
T table needs T;
Table_of_Foo table_of_foo needs Foo;
Then the linker can make sure *all* the statics in T or Foo are constructed
before table is constructed. (The first example might not need an explicit
dependency specification: The compiler could produce the obvious implicit
specification that a global of type T depends on T's statics. Or not: In both
examples, it might best be declared explicitly in the table's constructors
rather than here.)
Also, "needs Foo()" would imply dependence on the things that constructor
Foo() depends on:
struct Foo {Bar b; Foo() needs global_b {b = global_b;}};
// Rather than specifying implementation detail "needs global_b":
Foo *pf = (new Foo) needs Foo();
So, to summarize:
- <needs_clause> :==
needs <global variable name, class name, or constructor signature>
- We allow a <needs_clause>
- after a global variable definition, or
- before the body of a constructor definition.
- a <needs_clause> following a global variable definition applies to that
variable. A <needs_clause> in a Foo constructor definition causes the
compiler to emit the same <needs_clause> after the definition of any global
variable of type Foo or recursively containing subobjects of type Foo.
- "Foo foo needs <global object name>" tells the linker that the global object
must be constructed before global object `foo'.
- "Foo foo needs <class name>" tells the linker that all the statics in the
class must be constructed before `foo'.
- "Foo foo needs <constructor signature>" is equivalent to substituting the
<needs_clause> from the definition of the specified constructor.
> If a proposed scheme doesn't handle this case automatically and
> conveniently, there seems little point in burdening the language with half a
> solution.
On the contrary. Without any solution, globals become unusable! *Any*
function that uses a global might be called from someone's constructor.
Currently, there's no way to prevent such a function from being called before
the global is constructed. The programmer should have a way to prevent this.
Globals (including class statics) *are* useful things. It would be a pity if
they were unusable.
Author: pstemari@erinet.com (Paul J. Ste. Marie)
Date: Mon, 27 Mar 95 01:47:31 GMT Raw View
In article <3l4mv6$21q@hustle.rahul.net>,
"Ronald F. Guilmette" <rfg@rahul.net> wrote:
:You have not shown this class definition nested within any other
:scope, so we must assume that you meant to imply that this class
:type definition itself occurs at file scope.
:
:Given that, please note that the declaration of the `registry_'
:static data member is _only_ a declaration. It must be
:accompanied by a *definition* someplace else, and that
:*definition* will itself be treated as a file-scope object
:definition.
:
:In other words, to use your example to ask a question about the
:initialization of non-file-scope objects is nonsensical, because
:your example neither shows nor implies any such objects.
:
:Perhaps you had another example in mind(?)
Actually, I thought I had aborted this before posting it, but since
apparently I didn't I'll repose what I was going to say:
Does the WP address initialization of *file-scope* *non-objects*,
*initialized with constants*, say for example pointers?
class Foo {
private:
static Registry *registry_;
public:
static Registry *registry();
};
Registry *Foo::registry_ = 0;
Registry *Foo::registry() {
if (!registry_)
registry_ = new Registry;
return *registry_;
};
If Foo::registry_ has a guarantee of initialization prior to the
first call of Foo::registry() *in a static ctor*, then this
provides a general workaround for the order of static
initialization problem.
Some discussions over the last few days seem to indicate that this
is the case.
--Paul J. Ste. Marie, pstemari@well.sf.ca.us, pstemari@erinet.com
The Financial Crimes Enforcement Network claims that they capture every
public posting that has their name ("FinCEN") in it. I wish them good hunting.
Author: "Ronald F. Guilmette" <rfg@rahul.net>
Date: 27 Mar 1995 09:01:02 GMT Raw View
In article <3l55eg$sp8@news.erinet.com>,
Paul J. Ste. Marie <pstemari@erinet.com> wrote:
>In article <3l4mv6$21q@hustle.rahul.net>,
> "Ronald F. Guilmette" <rfg@rahul.net> wrote:
>:You have not shown this class definition nested within any other
>:scope, so we must assume that you meant to imply that this class
>:type definition itself occurs at file scope.
>:
>:Given that, please note that the declaration of the `registry_'
>:static data member is _only_ a declaration. It must be
>:accompanied by a *definition* someplace else, and that
>:*definition* will itself be treated as a file-scope object
>:definition.
>:
>:In other words, to use your example to ask a question about the
>:initialization of non-file-scope objects is nonsensical, because
>:your example neither shows nor implies any such objects.
>:
>:Perhaps you had another example in mind(?)
>
>Actually, I thought I had aborted this before posting it, but since
>apparently I didn't I'll repose what I was going to say:
>
>Does the WP address initialization of *file-scope* *non-objects*,
>*initialized with constants*, say for example pointers?
I dunno. I've never heard the term ``non-object'' before. What does
it mean?
--
-- 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: Mon, 27 Mar 1995 20:21:58 GMT Raw View
In article <FENSTER.95Mar26163432@ground.cs.columbia.edu>,
Sam Fenster <fenster@ground.cs.columbia.edu> wrote:
>
>I think the solution here must be to declare the table's dependency on the
>*class*, rather than on the private static member of the class:
>
A complete solution was in fact devised the problem of
global initialisation dependencies. It even worked for templates. Despite
encouraging further work on it with an explicit vote
of the committee, the proposal was dropped.
There were two problems. (1) it was untried.
(2) it would introduce link or compile time overhead.
So the status quo is that order of dynamic initialisation of
non-local variables is well defined only within a single translation unit.
This makes portable definition of constants of the type of a class
defined in a library which depend on other constants defined
in that library impossible.
--
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: alan@leek.com (Alan Rooks)
Date: 21 Mar 1995 16:45:30 GMT Raw View
Ronald F Guilmette <rfg@rahul.net> writes:
>>>> All I was saying is that I believe the final standard will say that
>>>> file-scope objects must all be fully constructed before `main' is
>>>> entered...
Jason Merrill <jason@cygnus.com> writes:
>>> Uh...why?
Ronald F Guilmette <rfg@rahul.net> writes:
>> Well, mostly because the ARM used to say some such thing.
>> Are you saying that this requirement has been removed from the drafts
>> Jason?
Jason Merrill <jason@cygnus.com> writes:
>Yes.
>
> 3.6.2 Initialization of non-local objects [basic.start.init]
>
> [paragraph removed for brevity]
As far as I *ever* knew (like 4 years ago) the ARM never said anything
about nonlocal statics necessarily being initialized before main() is
entered. The rule has always been that nonlocal statics will be initialized
before any use of a function or object defined in the file in which
the nonlocal static appears. In fact, the paragraph Jason quotes above
is almost identical to the one from my copy of the ARM (Oct 92 reprinting --
section 3.4, bottom of page 19), and I think previous reprintings, as well.
Alan
--
Alan Rooks Visual Edge Software, St-Laurent (Montreal)
alan@vedge.com (514) 332-6430 x623, FAX: (514) 332-5914
Author: donald@epix.net (Donald Way)
Date: Thu, 23 Mar 1995 04:51:19 -0500 Raw View
In article <795919326snz@thone.demon.co.uk>, andys@thone.demon.co.uk wrote:
> The problem here is that, depending on the order of initialisation of
> non-local objects, main() will get to pocess none,one or two Foos. I have
> code that does this (and has loads of very big comments mentioning this as
> implementation defined behaviour!). The technique is used to add new commands
> to a macro language. Want a new function? Write it, and link it. Done - no
> need to modify and recompile any existing code. Where my problem is, is that
> if the Foo neve registers itself, no code in it's source file will get
> executed, so the Foo won't get created, so it won't register itself, so no
> code in its file gets executed...etc.
Yes, I want this too. I ran into this coding event handlers on the
Macintosh where it is most convenient to be able to simply declare the
stuff you need in a file, add it to the project, and let the static
object's constructor do it's thing -- only nobody ever calls it so it
never calls itself. Sniff. I suggested to my vendor that they give us
something like a #pragma link_me_or_die, but they haven't gotten back to
me yet. :/
> I would prefer that it was gaurenteed that all non-local objects were
> initialised before main, since if we need finer control we can use the
> inline-function-and-static-object method.
I've run into this too, and started another thread on just this topic
(before I saw this one), but few are paying attention to it. Sniff. Just
what is this inline-function-and-static-object method you are talking
about? Or will you banish me to comp.lang.c++ to find out? :)
The problem I am having involves having two levels of dependency between
static objects declared in separate translation units. Exactly the same
problem you've outlined in your code sample, except for the fact that it
can't be resolved by simply forcing all the constructors to be executed
before main(). We need the ability to impose some very simple ordering on
constructors. I fully appreciate (now) the complexities in required the
compiler/linker to do this, but I still wonder why it should be so
difficult if the standard adopted a convention wherein these dependencies
could be specified using a separate source file (much like a makefile
already resolves source/header dependencies).
> Of course, this DOES give rise to problems with dynamically linked libraries,
> so maybe we need a new term - the execution unit, a level 'higher' than the
> translation unit? I dunno.
Yes again. Call it segment, module, whatever. On the platform I work
under, the problem of ordering constructors would be significantly
simplified were the requirement only to obey divisions by segment and not
translation unit. While not ideal, it would at least be workable, and it
would seem to solve everybody elses problem of getting the ctor call at
library/DLL/segment load time.
--
Donald Way
donald@epix.net
Author: chris@alofi.etca.fr (Christian Millour)
Date: 23 Mar 1995 10:16:35 GMT Raw View
In article <3kqrid$2h8@news.erinet.com>, pstemari@erinet.com (Paul J. Ste. Marie) writes:
|> In article <795919326snz@thone.demon.co.uk>,
|> andys@thone.demon.co.uk (Andy Sawyer) wrote:
|> : This will work fine, since Foo::registry_ will be initialised
|> :BEFORE the if(!registry_) gets evaluated. Where there is a problem
|> :is in something like this:
|>
|> [example deleted]
|>
|> This was exactly the sort of thing I had trouble with. My case was
|> even worse, in that my opcode was a dictionary object that mapped
|> string codes into build function pointers. When my actual opcodes
|> registered themselves before main executed, the program dumped core
|> because the dictionary object was being used before it was
|> constructed.
|>
|> I wound up fixing this by putting the definitions of the static
|> dictionary and the static subclass members that registered the
|> classes into the dictionary into the same file. This isn't so hot
|> from a maintenance perspective, since that file has to be modified
|> every time I subclassed my OpCode class, but at least it didn't
|> dump core.
|>
Similar problem, though in a dynamic-loading context. I wound up avoiding
static prototypes altogether and creating+registering/unregistering+destroying
prototypes in the body of the init/fini functions of the dso library...
Far from ideal but apparently safe (so far).
--chris@etca.fr
Le monde entier est un cactus, il est impossible de s'asseoir (J.Dutronc)
Author: pstemari@erinet.com (Paul J. Ste. Marie)
Date: Thu, 23 Mar 95 03:57:30 GMT Raw View
In article <795919326snz@thone.demon.co.uk>,
andys@thone.demon.co.uk (Andy Sawyer) wrote:
: This will work fine, since Foo::registry_ will be initialised
:BEFORE the if(!registry_) gets evaluated. Where there is a problem
:is in something like this:
[example deleted]
This was exactly the sort of thing I had trouble with. My case was
even worse, in that my opcode was a dictionary object that mapped
string codes into build function pointers. When my actual opcodes
registered themselves before main executed, the program dumped core
because the dictionary object was being used before it was
constructed.
I wound up fixing this by putting the definitions of the static
dictionary and the static subclass members that registered the
classes into the dictionary into the same file. This isn't so hot
from a maintenance perspective, since that file has to be modified
every time I subclassed my OpCode class, but at least it didn't
dump core.
Stuff like this is required for polymorphic persistence to work,
and the language has to make some provision for it.
Author: pstemari@erinet.com (Paul J. Ste. Marie)
Date: Wed, 22 Mar 95 17:56:52 GMT Raw View
In article <3kkq59$sq9@peippo.cs.tut.fi>,
esap@kaarne.cs.tut.fi (Pulkkinen Esa) wrote:
: e) You can't guaranteen that any file-scope objects have been
: constructed from different translation units during the
: construction of other file-scope objects. (the behaviour is
: implementation dependent).
This is really bad, and essentially makes a lot of static objects
unusable. Unfortunately, I can't think of a decent algorithm for
the link stage to get around this.
Does the WP address initialization of non-file-scope objects, say
for example pointers? If we can assume that the pointers get
initialized first, then you can do something like:
class Foo {
private:
static Registry *registry_;
public:
static Registry *registry();
};
Registry *Foo::registry_ = 0;
Registry *Foo::registry() {
if (!registry_)
registry_ = new Registry;
return registry_;
}
One of the uses for a static object of this sort is for subclasses
to registry themselves with the parent class so that queries
against secondary storage can return the proper mixture of
subclasses.
Author: jason@cygnus.com (Jason Merrill)
Date: 17 Mar 1995 18:49:30 GMT Raw View
>>>>> Ronald F Guilmette <rfg@rahul.net> writes:
> All I was saying is that I believe the final standard will say that
> file-scope objects must all be fully constructed before `main' is
> entered... and they must all be destructed _after_ a normal return
> from main.
Uh...why?
Jason
Author: thp@cs.ucr.edu (tom payne)
Date: 17 Mar 1995 19:14:17 GMT Raw View
Ronald F. Guilmette (rfg@rahul.net) wrote:
: All I was saying is that I believe the final standard will say that
: file-scope objects must all be fully constructed before `main' is
: entered... and they must all be destructed _after_ a normal return
: from main.
: This is inconsistant with the dynamic attachment of a shared library to
: a running process in the case where the shared library in question contains
: some of its own file-scope C++ objects (which presumably are initialized
: at attach-time, which is sometime _after_ process startup time, and which
: may even be at some point in time _after_ `main' has been entered).
: I think that most compiler vendors are aware of this troubling issue, but
: there is very little that can be done to reconcile C++'s traditional model
: of file-scope object initialization with the concept of dynamically-attachable
: shared libraries... at least not without doing some serious violence, both
: to our traditional understanding of how C++ works _and_ to the wording of
: the existing working paper.
What would be wrong with going to a construction-at-first-access
policy for file-scope globals. Even in the absence of dynamic
attachment, everyone has to resort to slightly unnatural idoms for
controlling the order of construction of such objects.
Tom Payne
Author: "Ronald F. Guilmette" <rfg@rahul.net>
Date: 18 Mar 1995 10:35:18 GMT Raw View
In article <JASON.95Mar17104930@phydeaux.cygnus.com>,
Jason Merrill <jason@cygnus.com> wrote:
>>>>>> Ronald F Guilmette <rfg@rahul.net> writes:
>
>> All I was saying is that I believe the final standard will say that
>> file-scope objects must all be fully constructed before `main' is
>> entered... and they must all be destructed _after_ a normal return
>> from main.
>
>Uh...why?
Well, mostly because the ARM used to say some such thing.
Are you saying that this requirement has been removed from the drafts
Jason? If so, it's news to me... but then I haven't been paying close
attention.
Tell me... if file-scope objects are _not_ constructed before `main' then
just when _are_ they constructed??
--
-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- E-mail: rfg@segfault.us.com ----------- Purveyors of Compiler Test ----
-------------------------------------------- Suites and Bullet-Proof Shoes -
Author: "Ronald F. Guilmette" <rfg@rahul.net>
Date: 18 Mar 1995 10:41:53 GMT Raw View
In article <3kcn29$evk@galaxy.ucr.edu>, tom payne <thp@cs.ucr.edu> wrote:
>Ronald F. Guilmette (rfg@rahul.net) wrote:
>: All I was saying is that I believe the final standard will say that
>: file-scope objects must all be fully constructed before `main' is
>: entered... and they must all be destructed _after_ a normal return
>: from main.
>
>: This is inconsistant with the dynamic attachment of a shared library to
>: a running process in the case where the shared library in question contains
>: some of its own file-scope C++ objects (which presumably are initialized
>: at attach-time, which is sometime _after_ process startup time, and which
>: may even be at some point in time _after_ `main' has been entered).
>
>: I think that most compiler vendors are aware of this troubling issue, but
>: there is very little that can be done to reconcile C++'s traditional model
>: of file-scope object initialization with the concept of dynamically-attachable
>: shared libraries... at least not without doing some serious violence, both
>: to our traditional understanding of how C++ works _and_ to the wording of
>: the existing working paper.
>
>What would be wrong with going to a construction-at-first-access
>policy for file-scope globals?...
Please provide a precise, unambiguous, and generally acceptable (to the
majority of X3J16 and WG21 members) definition of the term ``access''.
The C standard also uses the term ``access'', but the X3J11/WG14 members
were apparently smart enough to realize that they were incapable of de-
fining it precisely, so they didn't even try.
Even if we agree on a plausible (and usefully precise) definition of the
term ``access'' (which matches, in most important ways, our intutive ideas
about what an ``access'' is) there is still a bit of an implementation
problems with what you have suggested. How would you implement it
efficiently?
--
-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- E-mail: rfg@segfault.us.com ----------- Purveyors of Compiler Test ----
-------------------------------------------- Suites and Bullet-Proof Shoes -
Author: jason@cygnus.com (Jason Merrill)
Date: 18 Mar 1995 13:34:14 GMT Raw View
>>>>> Ronald F Guilmette <rfg@rahul.net> writes:
> In article <JASON.95Mar17104930@phydeaux.cygnus.com>,
> Jason Merrill <jason@cygnus.com> wrote:
>>>>>>> Ronald F Guilmette <rfg@rahul.net> writes:
>>> All I was saying is that I believe the final standard will say that
>>> file-scope objects must all be fully constructed before `main' is
>>> entered... and they must all be destructed _after_ a normal return
>>> from main.
>> Uh...why?
> Well, mostly because the ARM used to say some such thing.
> Are you saying that this requirement has been removed from the drafts
> Jason?
Yes.
> Tell me... if file-scope objects are _not_ constructed before `main' then
> just when _are_ they constructed??
3.6.2 Initialization of non-local objects [basic.start.init]
+------- BEGIN BOX 17 -------+
This is still under active discussion by the committee.
+------- END BOX 17 -------+
1 The initialization of nonlocal static objects (_basic.stc_) in a
translation unit is done before the first use of any function or
object defined in that translation unit. Such initializations
(_dcl.init_, _class.static_, _class.ctor_, _class.expl.init_) can 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. The default initialization of all static objects to
zero (_dcl.init_) is performed before any other initialization.
Static objects initialized with constant expressions (_expr.const_)
are initialized before any dynamic (that is, run-time) initialization
takes place. The order of initialization of nonlocal static objects
defined in the same translation unit is the order in which their defi-
nition appears in the translation unit. No further order is imposed
on the initialization of objects from different translation units.
Jason
Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: Sun, 19 Mar 1995 19:04:29 GMT Raw View
In article <JASON.95Mar18053414@phydeaux.cygnus.com>,
Jason Merrill <jason@cygnus.com> wrote:
>
>> Tell me... if file-scope objects are _not_ constructed before `main' then
>> just when _are_ they constructed??
>
> 3.6.2 Initialization of non-local objects [basic.start.init]
>
> +------- BEGIN BOX 17 -------+
> This is still under active discussion by the committee.
> +------- END BOX 17 -------+
>
>1 The initialization of nonlocal static objects (_basic.stc_) in a
> translation unit is done before the first use of any function or
> object defined in that translation unit. Such initializations
> (_dcl.init_, _class.static_, _class.ctor_, _class.expl.init_) can 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. The default initialization of all static objects to
> zero (_dcl.init_) is performed before any other initialization.
> Static objects initialized with constant expressions (_expr.const_)
> are initialized before any dynamic (that is, run-time) initialization
> takes place. The order of initialization of nonlocal static objects
> defined in the same translation unit is the order in which their defi-
> nition appears in the translation unit. No further order is imposed
> on the initialization of objects from different translation units.
Which clearly contains a certain amount of rubbish.
This clause is not normative. It is not properly expressed as a constraint
on the compiler, and if it were, it would impose an impossible
requirement.
>1 The initialization of nonlocal static objects (_basic.stc_) in a
> translation unit is done before the first use of any function or
> object defined in that translation unit. Such initializations
> takes place. The order of initialization of nonlocal static objects
> defined in the same translation unit is the order in which their defi-
> nition appears in the translation unit. No further order is imposed
These two sentences are contradictory:
extern int x;
extern int y;
int f() { return y; }
int g() { return x; }
int x = f();
int y = g();
main(){}
It is _impossible_ to initialise both x and y before one or the
other is "used".
What the clause _should_ say in my opinion is that
"Non-local variables requiring dynamic
initialisation shall be initialised in order of definition
within a translation unit.
"Each non-local variable shall have been so initialised
before the first use of the variable in an expression
executed after the body of "main()" has been invoked."
I.e. the compiler is not responsible for initialisation before the
first use if the use occurs _before_ main -- unless that use is
in the initialisation of a variable defined afterwards in the same
translation unit.
In other words, the _programmer_ is responsible for writing
code that works by not using forward declared variables to initialise
other variables.
--
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: esap@kaarne.cs.tut.fi (Pulkkinen Esa)
Date: 20 Mar 1995 20:56:09 GMT Raw View
Ronald F. Guilmette (rfg@rahul.net) wrote:
: Jason Merrill <jason@cygnus.com> wrote:
: >Ronald F Guilmette <rfg@rahul.net> writes:
: >
: >> All I was saying is that I believe the final standard will say that
: >> file-scope objects must all be fully constructed before `main' is
: >> entered... and they must all be destructed _after_ a normal return
: >> from main.
: >
: >Uh...why?
: Well, mostly because the ARM used to say some such thing.
I believe ARM didn't say such thing.
This is a quote from ARM: ( 3.4)
"The initialization of nonlocal static objects in a translation unit
is done before the first use of any function or object in that translation
unit. Such initialization 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."
This I believe means that the initialization of file-scoped objects may
or may not be done before the first statement of main.
: Are you saying that this requirement has been removed from the drafts
: Jason? If so, it's news to me... but then I haven't been paying close
: attention.
: Tell me... if file-scope objects are _not_ constructed before `main' then
: just when _are_ they constructed??
I had a lengthy discussion about this issue. We came to a conclusion, that
a) The wording in ARM has at best many interpretations.
b) The ARM doesn't specify the _exact_ time, when file-scoped objects are
constructed. I doubt that the WP (or whatever) will.
c) File-scope objects, that are in the same compilation unit as main()
has to be constructed (initialized) before the call to main, because
they have to be constructed before the first use of a function (in this
case, main) in the translation unit.
d) file-scope objects, that are in different translation units as main()
may be constructed before or after the initial call to main, but
it's required, that they are constructed before the first call of
a function in that translation unit from main() and before the first
object from that translation unit is created (in main() or in
functions called by main()).
e) You can't guaranteen that any file-scope objects have been constructed
from different translation units during the construction of other
file-scope objects. (the behaviour is implementation dependent).
But what this _doesn't_ say is that the initializations have to be done
"before main". In fact, it even allows the program to function for a long
time before these initializations are done, if no functions in the
translation unit are called and no objects of classes defined in that
translation unit are created. (that is, dynamic linking...)
This all is based on my interpretation of ARM, and if the 3.4 has been
changed since ARM, this all might have changed, so please correct me,
if I'm wrong.
--
Esa Pulkkinen | C++ programmers do it virtually
E-Mail: esap@cs.tut.fi | always with classes, resulting in
WWW : http://www.cs.tut.fi/~esap/ | multiple inheritance.
Author: swf@elsegundoca.ncr.com (Stan Friesen)
Date: Mon, 20 Mar 1995 20:17:34 GMT Raw View
In article <3keddh$g6f@hustle.rahul.net>, "Ronald F. Guilmette" <rfg@rahul.net> writes:
|>
|> Even if we agree on a plausible (and usefully precise) definition of the
|> term ``access'' (which matches, in most important ways, our intutive ideas
|> about what an ``access'' is) there is still a bit of an implementation
|> problems with what you have suggested. How would you implement it
|> efficiently?
|>
Well, the DLL's in SysVr4 have the capability of including a routine that
is run automatically when the library is loaded. It would be quite efficient
to make this init routine call the constructors for the file scope objects
in the library, and add them to the list of objects to be destroyed on exit.
Alternatively, the destructors could be called from the library's termination
function. [These routines are specified in the DT_INIT and DT_FINI entries
of the .dynamic section of a DLL according to the Sys. V Application Binary
Interface].
I really think it is VITAL that the C++ standard be written to allow this
implementation.
--
swf@elsegundoca.attgis.com sarima@netcom.com
The peace of God be with you.
Author: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 21 Mar 1995 11:16:01 -0000 Raw View
Stan Friesen (swf@elsegundoca.ncr.com) wrote:
: In article <3keddh$g6f@hustle.rahul.net>, "Ronald F. Guilmette" <rfg@rahul.net> writes:
: Well, the DLL's in SysVr4 have the capability of including a routine that
: is run automatically when the library is loaded. It would be quite efficient
: to make this init routine call the constructors for the file scope objects
: in the library, and add them to the list of objects to be destroyed on exit.
: Alternatively, the destructors could be called from the library's termination
: function. [These routines are specified in the DT_INIT and DT_FINI entries
: of the .dynamic section of a DLL according to the Sys. V Application Binary
: Interface].
: I really think it is VITAL that the C++ standard be written to allow this
: implementation.
If I understand this correctly, I second this wholeheartedly, because that
is exactly how DLLs on my platform (OS/2) work as well.
In fact, I often use a single global static-duration instance of a `DLL'
class to actually represent the DLL, and rely on the semantics of the C++
runtime to call the constructor at DLL load time and the destructor at DLL
unload time.
This is because the load order semantics of the DLL loader do not guarantee
that the _DLL_InitTerm function (which is similar in concept to the above two
functions) will be called *after* the C++ runtime DLL has been loaded and
initialised. I leave just the bare O/S interfacing in _DLL_InitTerm and
use the constructor and destructor of a static-duration class instance to
perform any DLL-level initialisation and cleanup that requires the C++
runtime (such as new and delete). i.e.
unsigned long module = 0 ;
extern "C"
unsigned long
_DLL_InitTerm ( unsigned long handle, unsigned long terminating )
{
if (!terminating) module = handle ;
}
class ThisDLL : public DLLClass {
public:
ThisDLL() ;
~ThisDLL() ;
} ThisDLL ;
ThisDLL::ThisDLL() : DllClass(module)
{
// Do initialisation that requires C++ RTL here
}
ThisDLL::~ThisDLL()
{
// Do termination that requires C++ RTL here
}
Changing the semantics of static-duration class instances to be initialised
and destroyed *relative to main* will screw this scheme up royally,
especialyl when it comes to run-time binding (as opposed to load-time
binding) to DLLs.
Author: "Ronald F. Guilmette" <rfg@rahul.net>
Date: 17 Mar 1995 09:18:50 GMT Raw View
In article <3k1vh8$2o0@galaxy.ucr.edu>, tom payne <thp@cs.ucr.edu> wrote:
>Ronald F. Guilmette (rfg@rahul.net) wrote:
>: ...
>: P.S. The very idea of using shared libraries (with file-scope static-storage
>: objects therein being initialized/constructed at attach-time) does, I believe,
>: rather conflict with what the emerging standard says/will say about the
>: timing of the construction of such objects. Oh well. All that means is
>: that when you put some of your compiled C++ code into a shared library,
>: you have chosen to use something which is, in effect, a non-conforming
>: implementation.
>
>
>Do I understand correctly that under the emerging standard views shared
>libraries as non-conforming? Is there some reason that they are less
>desirable than non-shared libraries of precompiled code, or are precompiled
>libraries in general discouraged?
All I was saying is that I believe the final standard will say that
file-scope objects must all be fully constructed before `main' is
entered... and they must all be destructed _after_ a normal return
from main.
This is inconsistant with the dynamic attachment of a shared library to
a running process in the case where the shared library in question contains
some of its own file-scope C++ objects (which presumably are initialized
at attach-time, which is sometime _after_ process startup time, and which
may even be at some point in time _after_ `main' has been entered).
I think that most compiler vendors are aware of this troubling issue, but
there is very little that can be done to reconcile C++'s traditional model
of file-scope object initialization with the concept of dynamically-attachable
shared libraries... at least not without doing some serious violence, both
to our traditional understanding of how C++ works _and_ to the wording of
the existing working paper.
--
-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- E-mail: rfg@segfault.us.com ----------- Purveyors of Compiler Test ----
-------------------------------------------- Suites and Bullet-Proof Shoes -
Author: "Ronald F. Guilmette" <rfg@rahul.net>
Date: 12 Mar 1995 18:27:18 GMT Raw View
In article <3jk7r7$6mc@silver.jba.co.uk>,
Jonathan de Boyne Pollard <JdeBP%utopium@jba.co.uk> wrote:
>Timothy Murphy (tmurphy@panix.com) wrote:
>: A _shared_ library should execute its static initializors at the
>: time it is installed, not at the time that yet another executable happens
>: to reference it. If you happen to be the unlucky soul who triggers
>: its installation, oh well.
>
>Not true on all platforms. I program on a platform where "shared
>libraries" (actually DLLs) are instantiated separately for every process
>that uses them, because process address spaces are (usually) disjoint. This
>means that static instances must be initialised in every process that links to
>the DLL.
I think you have been too lienient on Mr. Murphy. I would have said that
FOR ANY PLATFORM each process that connects to a shared library should get
its own private and FRESHLY INITIALIZED copy of the DATA REGION associated
with that shared library.
Shared library == shared code; not shared data.
P.S. The very idea of using shared libraries (with file-scope static-storage
objects therein being initialized/constructed at attach-time) does, I believe,
rather conflict with what the emerging standard says/will say about the
timing of the construction of such objects. Oh well. All that means is
that when you put some of your compiled C++ code into a shared library,
you have chosen to use something which is, in effect, a non-conforming
implementation.
--
-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- E-mail: rfg@segfault.us.com ----------- Purveyors of Compiler Test ----
-------------------------------------------- Suites and Bullet-Proof Shoes -
Author: thp@cs.ucr.edu (tom payne)
Date: 13 Mar 1995 17:31:20 GMT Raw View
Ronald F. Guilmette (rfg@rahul.net) wrote:
: ...
: P.S. The very idea of using shared libraries (with file-scope static-storage
: objects therein being initialized/constructed at attach-time) does, I believe,
: rather conflict with what the emerging standard says/will say about the
: timing of the construction of such objects. Oh well. All that means is
: that when you put some of your compiled C++ code into a shared library,
: you have chosen to use something which is, in effect, a non-conforming
: implementation.
Do I understand correctly that under the emerging standard views shared
libraries as non-conforming? Is there some reason that they are less
desirable than non-shared libraries of precompiled code, or are precompiled
libraries in general discouraged?
Tom Payne
Author: pete@borland.com (Pete Becker)
Date: Mon, 13 Mar 95 17:42:48 PST Raw View
In article <3k1vh8$2o0@galaxy.ucr.edu>, thp@cs.ucr.edu says...
>
>Do I understand correctly that under the emerging standard views shared
>libraries as non-conforming? Is there some reason that they are less
>desirable than non-shared libraries of precompiled code, or are
precompiled
>libraries in general discouraged?
The working paper says nothing at all about shared libraries. As
far as I know there is nothing in it that prevents the use of shared
libraries as an implementation technique.
-- Pete
Author: crayman@BIX.com (Cliff Rayman)
Date: Thu, 09 Mar 1995 00:04:33 Raw View
>: Furthermore, for some applications start-up time is very important.
>: Consider the example of Microsoft Word 6, which (I am told) takes several
>: minutes to invoke; if they were to defer some of the initialization until
>: later in the execution of the program, perhaps they wouldn't annoy quite
>so
>: many people.
Comes up in less than 3 seconds on a Pentium 100 :-)
Cliff Rayman
Rayman & Associates
Financial & Marketing Services for
Software Developers, Producers and Publishers
71401.1653@compuserve.com
Author: dak@cae.ca (Pierre Baillargeon)
Date: Fri, 10 Mar 1995 16:16:37 GMT Raw View
Jason Merrill (jason@cygnus.com) wrote:
: >>>>> Timothy Murphy <tmurphy@panix.com> writes:
: > [suggesting implicit initialization]
: I disagree. When you link with an archive library, you only get the
: modules that you use; when you link with a shared library, you get all of
: the modules that were used to build it. I think that it would be useful to
: be able to avoid running the constructors for modules that you never use.
: Furthermore, for some applications start-up time is very important.
: Consider the example of Microsoft Word 6, which (I am told) takes several
: minutes to invoke; if they were to defer some of the initialization until
: later in the execution of the program, perhaps they wouldn't annoy quite so
: many people.
Why should a language account for bad design and bad storage practice ?
People build up huge loadable libraries due to laziness and the absence of cost.
If C++ were to automatically initialize all, unrelated module would no longer
be lumped unneccessarily together. Modularity doesn't end with the compile
phase.
Author: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 8 Mar 1995 12:27:19 -0000 Raw View
Timothy Murphy (tmurphy@panix.com) wrote:
: A _shared_ library should execute its static initializors at the
: time it is installed, not at the time that yet another executable happens
: to reference it. If you happen to be the unlucky soul who triggers
: its installation, oh well.
Not true on all platforms. I program on a platform where "shared
libraries" (actually DLLs) are instantiated separately for every process
that uses them, because process address spaces are (usually) disjoint. This
means that static instances must be initialised in every process that links to
the DLL.
Not that this is too much of an issue anyway. It is very poor design to have
loads of global data in a DLL. (-:
: BTW, returning to the language standard, it says that non-local statics
: in a file F will be initialized before the program thread initiated by main()
: reaches any function in F. How could this even be implemented without
: invoking the constructors before main()?
Here is one way, just off the top of my head. I make no claims as to its
desirability or efficiency (although for large programs it may indeed be more
efficient than the "initialise everything before main() starts" approach if
lots of the program modules are rarely used).
static int __internal_compiler_magic_flag = 0 ;
static void __more_internal_compiler_magic ( void )
{
// Invoke constructors for all static-duration objects in this
// translation unit, and schedule their destructors to be invoked
// at process termination, somehow.
__internal_compiler_magic_flag = 1 ;
}
void A_user_function_in_this_module ( void )
{
if (!__internal_compiler_magic_flag)
__more_internal_compiler_magic() ;
// Body of function, as written by the programmer
}
Author: jason@cygnus.com (Jason Merrill)
Date: 07 Mar 1995 19:12:38 GMT Raw View
>>>>> Timothy Murphy <tmurphy@panix.com> writes:
> Good example. My real point is that these constructors be called
> without explicit source code invocation of any function in the file
> containing the statics. This behavior is _particularly_ useful in the
> case of loadable modules; as an aside, I actually wrote a dynamic
> loader (using HP C++) that did exactly that.
> I find the idiom of constructing a "language" through the assembly
> of object modules helpful, regardless of whether this assembly occurs
> "statically" at compile time or "dynamically" via run-time loading.
I disagree. When you link with an archive library, you only get the
modules that you use; when you link with a shared library, you get all of
the modules that were used to build it. I think that it would be useful to
be able to avoid running the constructors for modules that you never use.
Furthermore, for some applications start-up time is very important.
Consider the example of Microsoft Word 6, which (I am told) takes several
minutes to invoke; if they were to defer some of the initialization until
later in the execution of the program, perhaps they wouldn't annoy quite so
many people.
Jason
Author: tmurphy@panix.com (Timothy Murphy)
Date: 7 Mar 1995 16:58:16 -0500 Raw View
Jason Merrill (jason@cygnus.com) wrote:
: >>>>> Timothy Murphy <tmurphy@panix.com> writes:
: > Good example. My real point is that these constructors be called
: > without explicit source code invocation of any function in the file
: > containing the statics. This behavior is _particularly_ useful in the
: > case of loadable modules; as an aside, I actually wrote a dynamic
: > loader (using HP C++) that did exactly that.
: > I find the idiom of constructing a "language" through the assembly
: > of object modules helpful, regardless of whether this assembly occurs
: > "statically" at compile time or "dynamically" via run-time loading.
: I disagree. When you link with an archive library, you only get the
: modules that you use; when you link with a shared library, you get all of
: the modules that were used to build it. I think that it would be useful to
: be able to avoid running the constructors for modules that you never use.
A _shared_ library should execute its static initializors at the
time it is installed, not at the time that yet another executable happens
to reference it. If you happen to be the unlucky soul who triggers
its installation, oh well.
The case of an archive library is only slightly different from the
explicit inclusion of object files. My preference again would be to know
that the statics were initialized before main() was reached. I find
the idiom of interpreters/event handlers quite compelling.
: Furthermore, for some applications start-up time is very important.
: Consider the example of Microsoft Word 6, which (I am told) takes several
: minutes to invoke; if they were to defer some of the initialization until
: later in the execution of the program, perhaps they wouldn't annoy quite so
: many people.
I don't know how it is even possible to write a word processor that takes
several minutes to come up. This might be one of Microsoft's precious
few genuine innovations in software engineering. Somehow I think their
problems would not be solved simply by having access to a C++ compiler
that didn't initialize all the statics before main() was entered :-)
More seriously, in cases where initialization is expensive, I do
not think it is difficult to defer such initializations to a later
point in one's execution flow through explicit programming.
BTW, returning to the language standard, it says that non-local statics
in a file F will be initialized before the program thread initiated by main()
reaches any function in F. How could this even be implemented without
invoking the constructors before main()? If this really is the only
reasonable implementation, shouldn't we let people make good use of it?
I certainly have.
---tim
Author: jason@cygnus.com (Jason Merrill)
Date: 07 Mar 1995 00:05:34 GMT Raw View
>>>>> Timothy Murphy <tmurphy@panix.com> writes:
> (1) [DS 3.5.2] only guarantees that constructors for file or globally
> scoped objects in a file F are called before any code in F is invoked
> via the program thread initiated by main(). I propose that the standard
> instead guarantee that file and global statics in all source files be
> constructed before main() is called. This is the behavior of all compilers
> I know of
No, it isn't. Think dynamically loadable objects.
> (2) [DS 6.7] does _not_ specify that automatics be destroyed in
> the reverse order of declaration/construction nor does it specify the
> order of destruction of non-local statics.
I assume you mean "local statics" here.
3.6.3 Termination [basic.start.term]
1 Destructors (_class.dtor_) for initialized static objects are called
when returning from main() and when calling exit()
(_lib.support.start.term_). Destruction is done in reverse order of
initialization.
6.6 Jump statements [stmt.jump]
2 On exit from a scope (however accomplished), destructors
(_class.dtor_) are called for all constructed objects with automatic
storage duration (_basic.stc.auto_) (named objects or temporaries)
that are declared in that scope, in the reverse order of their decla-
ration.
18.3.2 exit [lib.exit]
--Next, all static objects are destroyed in the reverse order of their
construction. (Automatic objects are not destroyed as a result of
calling exit.)131)
Happy?
> (3) [DS 15.2] promises that automatics are destroyed when an
> exception is thrown "through" their stack frame and, importantly, that
> the constructed base objects and subobjects of a partially constructed
> automatic are also destroyed in this event. While the destruction of
> a fully constructed heap object is obviously the responsibility
> of the programmer (at least in the absence of garbage collection),
> partially constructed heap objects _must_ have their fully constructed
> bases and subobjects destroyed by the run-time system when an exception
> is thrown "through" their constructors (i.e. partially constructed heap
> objects need exactly the same guarantee as do partially constructed
> automatics). This is obvious; there is no reasonable way to "program
> around" the absence of this guarantee.
I agree that this could be clearer, but I think the intent is there:
15.2 Constructors and destructors [except.ctor]
1 As control passes from a throw-point to a handler, destructors are
invoked for all automatic objects constructed since the try-block was
entered.
2 An object that is partially constructed will have destructors executed
only for its fully constructed sub-objects. Should a constructor for
an element of an automatic array throw an exception, only the con-
structed elements of that array will be destroyed. If the object or
array was allocated in a new-expression, the storage occupied by that
object is sometimes deleted also (_expr.new_).
Jason
Author: tmurphy@panix.com (Timothy Murphy)
Date: 7 Mar 1995 12:34:14 -0500 Raw View
Jason Merrill (jason@cygnus.com) wrote:
: >>>>> Timothy Murphy <tmurphy@panix.com> writes:
: > (1) [DS 3.5.2] only guarantees that constructors for file or globally
: > scoped objects in a file F are called before any code in F is invoked
: > via the program thread initiated by main(). I propose that the standard
: > instead guarantee that file and global statics in all source files be
: > constructed before main() is called. This is the behavior of all compilers
: > I know of
: No, it isn't. Think dynamically loadable objects.
Good example. My real point is that these constructors be called
without explicit source code invocation of any function in the file
containing the statics. This behavior is _particularly_ useful in the
case of loadable modules; as an aside, I actually wrote a dynamic
loader (using HP C++) that did exactly that.
I find the idiom of constructing a "language" through the assembly
of object modules helpful, regardless of whether this assembly occurs
"statically" at compile time or "dynamically" via run-time loading.
: > (2) [DS 6.7] does _not_ specify that automatics be destroyed in
: > the reverse order of declaration/construction nor does it specify the
: > order of destruction of non-local statics.
: I assume you mean "local statics" here.
yes, pardon the slip.
: 3.6.3 Termination [basic.start.term]
: 1 Destructors (_class.dtor_) for initialized static objects are called
: when returning from main() and when calling exit()
: (_lib.support.start.term_). Destruction is done in reverse order of
: initialization.
: 6.6 Jump statements [stmt.jump]
: 2 On exit from a scope (however accomplished), destructors
: (_class.dtor_) are called for all constructed objects with automatic
: storage duration (_basic.stc.auto_) (named objects or temporaries)
: that are declared in that scope, in the reverse order of their decla-
: ration.
I guess the draft spec I read is a little out of date; I'm not always
as well read as I should be. Thanks for the citation.
: 18.3.2 exit [lib.exit]
: --Next, all static objects are destroyed in the reverse order of their
: construction. (Automatic objects are not destroyed as a result of
: calling exit.)131)
: Happy?
Somewhat. The actual order of construction of statics is only known
at run-time (and these objects should of course be destroyed in the reverse
of this run-time order); I would be happier if this fact were more explicitly
acknowledged in the standard.
: > (3) [DS 15.2] promises that automatics are destroyed when an
: > exception is thrown "through" their stack frame and, importantly, that
: > the constructed base objects and subobjects of a partially constructed
: > automatic are also destroyed in this event. While the destruction of
: > a fully constructed heap object is obviously the responsibility
: > of the programmer (at least in the absence of garbage collection),
: > partially constructed heap objects _must_ have their fully constructed
: > bases and subobjects destroyed by the run-time system when an exception
: > is thrown "through" their constructors (i.e. partially constructed heap
: > objects need exactly the same guarantee as do partially constructed
: > automatics). This is obvious; there is no reasonable way to "program
: > around" the absence of this guarantee.
: I agree that this could be clearer, but I think the intent is there:
: 15.2 Constructors and destructors [except.ctor]
: 1 As control passes from a throw-point to a handler, destructors are
: invoked for all automatic objects constructed since the try-block was
: entered.
: 2 An object that is partially constructed will have destructors executed
: only for its fully constructed sub-objects. Should a constructor for
: an element of an automatic array throw an exception, only the con-
: structed elements of that array will be destroyed. If the object or
: array was allocated in a new-expression, the storage occupied by that
: object is sometimes deleted also (_expr.new_).
I would find it comforting if the second paragraph explicitly included
the case of objects on the heap.
: Jason
---tim
Author: tmurphy@panix.com (Timothy Murphy)
Date: 6 Mar 1995 12:51:06 -0500 Raw View
Some Suggestions regarding Implicitly Invoked Constructors and Destructors.
Timothy S. Murphy 5 March 1995.
A few observations and proposals regarding the C++ language standard.
The last draft specification (DS) I looked at was dated 20 September 1994;
I also refer to the ARM.
Please rip this apart, both with your editor and your words; I am
very interested in feedback, particularly from members of the standards
committee.
(1) [DS 3.5.2] only guarantees that constructors for file or globally
scoped objects in a file F are called before any code in F is invoked
via the program thread initiated by main(). I propose that the standard
instead guarantee that file and global statics in all source files be
constructed before main() is called. This is the behavior of all compilers
I know of and it is in fact quite useful to rely on this behavior.
The problem with the existing guarantee can be seen as follows.
What if the code in F could only be reached via one of its static objects?
For example, I have written an interpreter-based application whose basic
idiom is for C++ files to augment the ambient interpreter (which happens to
be TCL) with commands implemented in that file; this is accomplished by having
the commands be file-scope instances of a class whose constructor installs
the command in the ambient interpreter. It is very convenient to rely
on static constructors for all files being called before main(). This
allows languages to be constructed purely by aggregating object files
in a makefile _without_ touching any piece of source code (in particular,
without explicitly calling initialization functions from main()).
This idiom has obvious applicability to any interpreter or event based
application and deserves to be supported.
One may similarly argue that function-scoped statics (i.e., locals)
should be initialized before main() is called rather than on the occasion
of the first entry to the function [DS 6.7] though I feel less strongly
about it.
(2) [DS 6.7] does _not_ specify that automatics be destroyed in
the reverse order of declaration/construction nor does it specify the
order of destruction of non-local statics. I propose that the standard
instead contain a blanket guarantee that _all_ implicit destructors be
called in the reverse order from that of the corresponding constructors.
A precise way of saying this is that if the run-time system implicitly
destroys A and later destroys B, then B was constructed before A.
This is simply a matter of sanity and is necessary if we wish A
to safely refer to B; this reference is certainly possible at the time
of construction. Note also that it is _never_ wrong to destroy objects
in reverse order of construction and that it may very well be wrong
to do otherwise.
This behavior is easily implemented by use of a stack. Assuming that
local statics are constructed before main() is called, one could keep
all objects having nontrivial destructors on a huge stack with all the
statics on the bottom, automatics above, pushing objects as you construct
them, popping objects as they are destroyed. This stack approach also
seems to simplify exception handling.
It is already guaranteed for temporaries [DS 12.2] as well as for
nonlocal statics [DS 12.6.1]. I remark that [DS 12.6.1] also promises
that nonlocal statics are constructed in the order of their appearance
in a file; this is not always possible and I assume that the destruction
order is the opposite of the actual run-time construction order and
not simply the reverse order of their source code appearance. Perhaps
this latter point should be clarified in the language spec.
(3) [DS 15.2] promises that automatics are destroyed when an
exception is thrown "through" their stack frame and, importantly, that
the constructed base objects and subobjects of a partially constructed
automatic are also destroyed in this event. While the destruction of
a fully constructed heap object is obviously the responsibility
of the programmer (at least in the absence of garbage collection),
partially constructed heap objects _must_ have their fully constructed
bases and subobjects destroyed by the run-time system when an exception
is thrown "through" their constructors (i.e. partially constructed heap
objects need exactly the same guarantee as do partially constructed
automatics). This is obvious; there is no reasonable way to "program
around" the absence of this guarantee.
[DS 5.3.4] does specify that if an exception is thrown inside a
"new" expression, any memory allocated is freed but says nothing about
the invocation of destructors.
-- Timothy S. Murphy: A serious user and admirer of C++.
tmurphy@panix.com