I wondered if it was possible to control the creation order of global
static objects in a C++ program. These objects are created before main
executes but in which order? And are they destructed in reverse order?
I know that for static objects declared in functions, creation occurs when
the program flow goes for the first time through the function, but how can
I determine the order when objects are declared in different .cpp files?
I think there is no way to determine the order, but could someone confirm
or explain why I am wrong?
YvDi.
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
1) unspecified 2) in reverse order of their construction
> I know that for static objects declared in functions, creation occurs when
> the program flow goes for the first time through the function, but how can
> I determine the order when objects are declared in different .cpp files?
You can't. However, you don't need to either : just wrap every access
as a function call, e.g. instead of
Foo.cc:
Foo theFoo(...);
Bar.cc
Bar aBar(theFoo); // theFoo must have been constructed at that point
use
Foo.cc
Foo & theFoo() { static Foo x(...); return x; }
Bar.cc
Bar aBar(theFoo());
HTH
There is very good explanation in Scott Meyers "Efective C++: 50 Specific
Ways to Improve Your Programs and Designs (Addison-Wesley Professional
Computing Series)" about globals creation order (or better, why order can
not be determined). You'll also find a perfect solution there if your
program depends on globals.
gorc
YvDi wrote in message <01be21c2$e22fad60$4705...@yvdi.innet.be>...
>Dear all,
>
>I wondered if it was possible to control the creation order of global
>static objects in a C++ program. These objects are created before main
>executes but in which order? And are they destructed in reverse order?
[snp]
Assuming that you know all the problems with globals/statics in multi-
threaded contexts and still want them.
There is no way of forcing an order of initialisation unless you put
them all in a single file (it is the order of file linkage that lies at
the root of the problem) there is an idiom that solves the problem in
the sense that it guarantees just in time initilaisation:
replace each global variable of the form:
T t= <initialiser>;
by
inline
T & t(){
static T t_= <initialiser>;
return t_;
}
Francis Glassborow Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
>Dear all,
>
>I wondered if it was possible to control the creation order of global
>static objects in a C++ program. These objects are created before main
>executes but in which order? And are they destructed in reverse order?
I don't believe that the order can be assured unless you test it on your
particular platform, but it may be different on other platforms. In any
case, they will be destructed in reverse order.
Why would it be important to know the order of construction?
--
Daniel T.
dani...@gte.net
http://home1.gte.net/danielt3/
1) is only partially accurate. It's specified by the standard to be in
order
of declaration _within translation units_. Accross translation units it's
unspecified.
-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own
>replace each global variable of the form:
>
>T t= <initialiser>;
>
>by
>
>inline
>T & t(){
> static T t_= <initialiser>;
> return t_;
>}
Generally, the best method. Two things to point out.
First, this is the method to 'define' non-integral constants inside
a class. For example:
struct X { static const double x=3.5; }; // error
struct X { static double x() { return 3.5; } }; // ok
Second, the use of a static variable inside the function has one
advantage and one disadvantage:
[+] The instance is only created if the function is called at least
once. This means that if you don't use the static variable, it
is not created.
[-] Each time you call the function, the function must check to see
if the object is already created. This is ok for the first
call. But successive calls suffer a performance hit. Usually
this performance hit is negligible.
So here's another method. This method creates the object at program
startup. This means that it always creates the object. But it
also means that the access functions can assume that the object
already exists. This is the counter technique from Lakos.
Here's a brief summary:
There is a class called Init
The class has a static variable 'count' of type 'unsigned'
The ctor Init() increments 'count'
The dtor ~Init() decrements 'count'
In the ctor Init(), if count==1, initialization happens
In the dtor ~Init(), if count==0, de-initialization happens
Each translation unit that requires the initialization to happen
defines an instance of an Init.
To understand this program, note that 'const' objects have internal
linkage by default. This means that if a header file defines a
const variable, each translation unit that includes the header
gets its own static copy of the variable. (Recall that direct use
of the keyword 'static' to give an object internal linkage is
deprecated.) So you don't get multiple definition errors at link
time.
// Counter.h
template <class T>
struct Counter
{
Counter() { s_count++; }
Counter(const Counter&) { s_count++; }
~Counter() { s_count--; }
static unsigned count() { return s_count; }
private: static unsigned s_count;
};
template <class T> unsigned Counter<T>::s_count;
// Singleton.h
#if !defined(Singleton_h)
#define Singleton_h
#include "Counter.h"
class Singleton
{
public:
struct Init : private Counter<Init>
{
Init()
{
if (count()==1) Singleton::s_instance=new Singleton;
}
~Init()
{
if (count()==0) delete Singleton::s_instance;
}
};
friend Init;
static Singleton& instance() { return *s_instance; }
int val() { return ++num; }
private:
Singleton(); // sets num to 2
~Singleton();
int num;
static Singleton * s_instance;
Singleton(const Singleton&); // not implemented
Singleton& operator=(const Singleton&); // not implemented
};
const Singleton::Init init_Singleton; // internal linkage
#endif
// Singleton.c
#include "Singleton.h"
#include <iostream.h>
Singleton * Singleton::s_instance;
Singleton::Singleton() : num(2) { cout << "Singleton()\n"; }
Singleton::~Singleton() { cout << "~Singleton()\n"; }
// COMPILE WITH: como -c Singleton.c
// note: because "Singleton.h" included,
// there is an instance of a Singleton::Init in Singleton.o
// it has the name init_Singleton but has internal linkage
// if the static variables in Singleton.o initialized first,
// then this init_Singleton is initialized first
// so the real object Singleton::s_instance is created first
// Module.h
#include "Singleton.h"
struct Module
{
Module();
~Module();
int three;
int four;
static Module module;
};
// Module.c
#include "Module.h"
#include "Singleton.h"
#include <iostream>
Module::Module()
{
cout << "Module()\n";
three=Singleton::instance().val();
four =Singleton::instance().val();
}
Module::~Module() { cout << "~Module()\n"; }
Module Module::module;
// COMPILE WITH: como -c Module.c
// note: because "Singleton.h" included ("Module.h" includes it)
// there is an instance of a Singleton::Init in Module.o
// it has the name init_Singleton but has internal linkage
// so it is different from the init_Singleton in Singleton.o
// if the static variables in Module.o initialized first,
// then this init_Singleton is initialized first
// so the real object Singleton::s_instance is created first
// main.c
#include "Singleton.h"
#include "Module.h"
#include <iostream.h>
int main()
{
cout << Module::module.three << ' ' << Module::module.four << '\n';
}
Compile with: como -c main.c Module.o Singleton.o
Also try : como -c main.c Singleton.o Module.o
On como, if you compile with the first method, the static objects in
Module.o are initialized first, and then the static objects in
Singleton.o. This means that if you don't use the counter technique
-- that is, you have an instance variable
static Singleton::s_instance;
and the instance() function just returns this s_instance, then the
output you see is not
3 4
For me, the output was
1 2
But if you use the counter technique, you get the right output
3 4
--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------
Not strictly true. Within a single module, globals are initialized in
the order they appear. You lose control, however, once you've got
globals in multiple modules. However, it is often the case that you
don't care about the init order of unrelated objects, and related ones
can be kept together in single modules.
That said, it is also possible on some platforms to control
initialization order among modules by explicitly controlling link order.
However, this is nonstandard. I've found that Watcom inits later modules
first, while Borland inits earlier modules first. (Those are the only
two compilers I've checked.) I think Watcom's method is right, because
the usual mechanism for pulling in modules from libraries tends to
result in the lowest-level modules being pulled in last, and those
generally need to be initialized first.
--
Ciao,
Paul
For example:
// file1.cpp
#include "sometype.h"
sometype a;
sometype b;
// file2.cpp
#include "sometype.h"
sometype c;
'a' will be initialized before 'b' - this is guaranteed by the Standard. But
you have absolutely no control over the initialization of 'c' - it may be
initialized before 'a', or after 'b' (or even, I suppose, after 'a' and before
'b' - that still falls within the rules, but it is highly unlikely).
See the faq (http://www.cerfnet.com/~mpcline/c++-faq-lite/) for a discussion
of this issue and some suggestions on how to resolve it.
> Why would it be important to know the order of construction?
It is quite common to have an object in one translation unit depend on an
object in another translation unit being initialized first.
--
Jim
Note to recruitment agencies: I delete unsolicited email from
recruitment agencies without reading it, so don't even bother.
-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
As others have noted, the Standard requires that (a) global static
objects within a given Translation Unit (TU - a single compilation) be
constructed in textual order; (b) destructors execute in the reverse of
the order of construction. There's also a requirement that EITHER all
global static objects are created before main() is entered OR all global
static objects within a TU are executed before any function in that TU
is executed, or something of that sort. The two disjuncts here are
really different - you can construct initialization examples that obey
either without obeying the other.
The Standard places *no* constraints on the order in which different
TU's execute their global static constructors. Hence, a portable
program may make no such assumption.
Individual implementations may allow you further control, usually
through link-time options. For example, the AIX linker lets you set an
"initialization priority". (Actually, the AIX C++ compiler has a pragma
that lets you control this even more closely.) Solaris, on the other
hand, doesn't appear to provide you with any way to control the priority
except by controlling the order in which you link a program.
If you want to write even vaguely portable code, do *not* rely on the
order in which static global constructors will run.
-- Jerry
Sometimes, you will have dependent global variables, where, the
initialization of one need to access another variable which has to be
initialized already.
eg:
class Foo {};
class Bar {
public:
Bar ( const Foo& aFoo ) : myFoo(aFoo) {}
private:
Foo& myFoo;
};
// Global variables;
Foo x;
Bar y(x);
Regards,
Biju Thomas
>There is no way of forcing an order of initialisation unless you put
>them all in a single file (it is the order of file linkage that lies at
>the root of the problem) there is an idiom that solves the problem in
>the sense that it guarantees just in time initilaisation:
>
>replace each global variable of the form:
>
>T t= <initialiser>;
>
>by
>
>inline
>T & t(){
> static T t_= <initialiser>;
> return t_;
>}
Just to add a word of caution: Some compilers (last time I checked,
MSVC was in that set) would fail this idiom if t() is called from
several threads. In particular, if T::T() takes a lot of time, your
best bet is to make sure t() is called by your primary thread *before*
other threads start to run.
---------------------------------------------
Ziv Caspi
zi...@netvision.net.il
I'm affraid it's not the best method. Get this:
class Keyboard { ...};
class Log() {...};
// Your Singleton accessors here
Keyboard::~Keyboard()
{
if (!ShutDown())
theLog().Report("Something weird happened.");
}
Now if theKeyboard() was called before theLog() - which can be pretty
likely, if the log wasn't used so far - the program crashes in flames.
The Singleton problem is far, far away from simple.
Andrei
But the function includes static data and so is dangerous in a multi-
threaded program. In such cases you need to do more (but at least you
have some encapsulation to provide some control, globals are really bad
news in an MT environment)
Francis Glassborow Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
In the Singleton case you can getaway by using that "doble-checking
pattern".
Andrei
So what is?
>
>class Keyboard { ...};
>class Log() {...};
>
>// Your Singleton accessors here
>
>Keyboard::~Keyboard()
>{
> if (!ShutDown())
> theLog().Report("Something weird happened.");
>}
>
>Now if theKeyboard() was called before theLog() - which can be pretty
>likely, if the log wasn't used so far - the program crashes in flames.
>The Singleton problem is far, far away from simple.
This kind of dependency in dtors is evil. If you must have it then you
are going to have to provide explicit instantiations (which you can if
you use functions rather than naked globals. Just add an init()
function that is the first function called by main.
Francis Glassborow Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
>But the function includes static data and so is dangerous in a multi-
>threaded program. In such cases you need to do more (but at least you
>have some encapsulation to provide some control, globals are really bad
>news in an MT environment)
So do you think the counter method would be work correctly for
situations like these? For example, if we have two singleton
objects -- ExponentialTable and FermiIntegrals -- and
FermiIntegrals requires ExponentialTable to be initialized
first, then we just do this:
// ExponentialTable.h
class ExponentialTable { ... }; // singleton
const ExponentialTable::Init ExponentialTable_;
// the first static object in ExponentialTable.o
// will be an ExponentialTable_ of internal linkage
// FermiIntegrals.h
#include "ExponentialTable.h"
// this include directive important
// it ensures that the first static object in FermiIntegrals.o
// will be an ExponentialTable_ of internal linkage
class FermiIntegrals { ... }; // singleton, uses ExponentialTable
const FermiIntegrals::Init FermiIntegrals_;
// the second static object in FermiIntegrals.o
// will be a FermiIntegrals_ of internal linkage
Even if class FermiIntegrals doesn't need ExponentialTable (eg,
the member functions of class FermiIntegrals are defined in the
.cpp file), we still need the include directive. The include
directive ensures that the first static object in FermiIntegrals.o
is an ExponentialTable::Init, which basically means that the
ExponentialTable will be initialized first.
Actually, we could leave out the #include, but the constructor
of FermiIntegrals::Init needs to do a little more work:
// FermiIntegrals.h
class FermiIntegrals { ... }; // singleton
class FermiIntegrals : private Counter<FermiIntegrals>
{
FermiIntegrals();
~FermiIntegrals();
};
const FermiIntegrals::Init FermiIntegrals_;
// this will be first static object in FermiIntegrals.o
// FermiIntegrals.c
#include "FermiIntegrals.h"
#include "ExponentialTable.h"
// second static object in FermiIntegrals.o is ExponentialTable_
namespace { const ExponentialTable::Init * init; }
FermiIntegrals::FermiIntegrals()
{
if (count()==1)
{
init=new ExponentialTable::Init;
FermiIntegrals::s_instance=new FermiIntegrals();
}
}
FermiIntegrals::~FermiIntegrals()
{
if (count()==0)
{
delete FermiIntegrals::s_instance;
delete init;
}
}
--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
> In article <366f7af1....@news.netvision.net.il>, Ziv Caspi
> <zi...@peach-networks.com> writes
> >Just to add a word of caution: Some compilers (last time I checked,
> >MSVC was in that set) would fail this idiom if t() is called from
> >several threads. In particular, if T::T() takes a lot of time, your
> >best bet is to make sure t() is called by your primary thread *before*
> >other threads start to run.
>
> But the function includes static data and so is dangerous in a multi-
> threaded program. In such cases you need to do more (but at least you
> have some encapsulation to provide some control, globals are really bad
> news in an MT environment)
If you write class T properly, the fact that t is static does not mean
it cannot (or should not) be MT-unsafe. For instance, I've often used
shared MT-safe objects as queue mechanisms between threads. One thing
to make sure is that all T's public methods are properly guarded (MT-safe).
The constructor is unique (which is what I was referring-to), because
there is usually no reasonable way to lock the constructor -- the most
elegant solution is to make sure it is called when there's only a single
thread in existence.
(Other solutions are, of course, possible. However, all the ones I'm
familiar with are even worse than the one I presented.)
--------------------------------
Ziv Caspi
zi...@netvision.net.il
>In the Singleton case you can getaway by using that "doble-checking
>pattern".
What is this pattern? (It seems to me that the word pattern is
overused.) Who does the checking?
--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]