Stroustrup (in "The C++ P. L.", 3rd ed., chapter 21.5.2) presents a
"technique that is general enough to be used to ensure proper order of
construction and destruction of global objects". He uses a helper class
with a "first time switch" constructor to initialize the global object.
Each compilation unit gets its own instance of this class (via the
included header) to establish the desired order locally. Here's the code
from the book:
-- snip
class ios_base::Init {
static int count;
public:
Init();
~Init();
};
namespace { // in <iostream>, one copy in each file #including
<iostream>
ios_base::Init __ioinit;
}
int ios_base::Init::count = 0; // in some .c file
ios_base::Init::Init()
{
if (count++ == 0) { /* initialize cout, cerr, cin, etc. */ }
}
-- snip
OK, but how do you initialize the objects? Is it possible to do
an assignment like
cout = std::ostream(...);
even though cout might not yet be constructed (I guess not)?
I know the problem could be solved with an accessor function that
returns a local
static object but that requires writing the parens every time the object
is used, which can be a nuisance.
My compiler (MSVC++ 5) offers the "init_seg" pragma which (I suppose)
was used for their implementation of <iostream>, but is this portable?
Any help appreciated,
Stephan
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
>Is there a technique to ensure that a particular global object gets
>constructed first (across multiple compilation units)? Canonical
>examples for such objects would be the streams std::cout etc. from
><iostream>.
>Stroustrup (in "The C++ P. L.", 3rd ed., chapter 21.5.2) presents a
>"technique that is general enough to be used to ensure proper order of
>construction and destruction of global objects".
It was first presented in the ARM (page 19), and if often called the
"nifty-counter" technique, after a name used in the example.
It is the technique often used for implementing iostreams,
to ensure that the standard streams (cin, cout, cerr) are
constructed before any objects that use them. It isn't
foolproof, and programmers sometimes manage to create
cross-dependencies that cause program failure during
startup.
In addition, the technique destroys program locality when
a helper object is created in every module during startup.
It causes your entire program to be paged in before the
first statement of main can run. On really big programs,
I"ve heard of startup taking a LOT of extra time.
You can avoid the problems by not using global static
objects, with some small extra costs.
Instead of creating a global object, use a local static
ebject that is accessed only by a function call. For example,
instead of
T Tobject(args);
write
T& Tobject()
{
static T t(args);
return t;
}
You access the object as "Tobject()" instead of just "Tobject".
The very first time the object is needed, no matter from where
it it accessed, the object is created. It is automatically
destroyed at program end, after any objects that accessed it.
The object is not initialized until it is first needed, and
no extra code to create helpers appears in the program.
One disadvantage is that you must use function-call syntax
to access the object. That is why iostreams doesn't use the
technique: you would have to write for example
cout() << "Hello, world!";
which would have been a drastic usage change from streams as
introduced originally in C++, and would conflict with
what would be typical for programmer-created streams:
ofstream myfile("file.txt");
myfile << "Goodbye, cruel world!"; // not: myfile()
In the absence of extern inline functions (which are in the
standard but not widely supported yet), you also need an
actual (but cheap) function call to access the object. With
inline extern functions, the runtime call can be elmininated.
Finally, the local-static technique does not ensure the
object will ever be created -- if it is never referenced,
it will not be created or destroyed. (That might be an
advantage, of course.)
>My compiler (MSVC++ 5) offers the "init_seg" pragma which (I suppose)
>was used for their implementation of <iostream>, but is this portable?
Some systems allow you to create global objects in multiple
modules, and the linker will discard duplicates. (Typically
you create the objects in a special address section.) You
would then, to continue the iostream example, simply create
a global copy of the standard stream objects (cin, cout, cerr) in
every module that included <iostream>. No helper classes or
static counters would be needed. At link time, all but one
object is discarded, and the references from other modules
are adjusted to refer to the single copy that is left. This
is the best solution for global objects, but it isn't portable.
Not all systems support the technique, and the way you get
the result on supported systems varies.
--
Steve Clamage, stephen...@sun.com
Your guess is correct. Consider using placement new to construct the
object. Also worry about constructing more than once. Perhaps the
default
constructor just does
the counter trick. The counter trick code uses placement new (with a
non-default constructor) to perform the actual initialization.
There's a subtlety with this approach which, while strictly outside the
scope of the C++ standard, is important to keep in mind: Correctness in
multi-threaded applications. Suppose multiple threads simultaneously
call Tobject() before t has been initialized. Will the generated code
that checks to see if t has been initialized yet work properly in multi-
threaded environments? What will happen if not, and t's initialization
code runs in multiple threads at the same time, or even consecutively?
Part of the initialization process will consist of adding t to some
global list of objects to be destructed at termination. Will it end up
on the list twice?
For global objects initialized at program startup, these are not a
problem, since there won't typically be multiple threads at program
startup. They can return, even for such objects, if you load libraries
dynamically. Fortunately, I've yet to see a dynamic loading facility
that isn't internally single-threaded (so even if you *do* call it from
multiple threads, it will execute your load requests sequentially).
If you're using Posix threads, the only safe way to deal with this is to
use pthread_once to call an initialization function. Win32 doesn't have
a direct analogue of pthread_once; you have to implement it yourself,
using a mutex. The code is a bit trickier than you'd expect, but can be
done.
Since you can't prevent the C++ run-time from trying to execute t's
constructor, you'll actually have to keep around a pointer to a T,
allocating and filling in the pointer in the once() function. And then
of course you have to set up an appropriate object to be destructed
later if you want the object to be deleted.
I have yet to see documentation for any C++ compiler that touches on
these issues. Perhaps some of the compilers I'm using actually do
thread-safe initialization for statics - I can't tell. Until all the
C++ compilers I have to deal with start guaranteeing it, I'll have to
continue initializing things myself.
-- Jerry
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
To some extent, it's up to the class designer how much initialization of
an object will be done inside the constructor and how much will be
deferred to a separate initialization function. The exceptions are the
initialization of reference and const data members (which are rarely
necessary), and the automatic initialization of the virtual function
table pointer (which is unavoidable).
The only thing I can think of is to design the class so that it is
harmless to run the constructor twice. Then, in the Init::Init function,
the various objects that need early constructing can be explicitly
constructed with:
new (&cout) iostream(...);
Of course, at some point, the system is going to call the constructor
itself (unless cout is hacked by declaring it in an assembly language
module--ugh!). But it is pretty easy to make the constructor notice that
it has already been called, with a private bool that is set by the
constructor. This relies upon the fact that memory is cleared before
anything else runs.
> My compiler (MSVC++ 5) offers the "init_seg" pragma which (I suppose)
> was used for their implementation of <iostream>, but is this portable?
No. Borland has a similar pragma, but it's not identical.
--
Ciao,
Paul
>It was first presented in the ARM (page 19), and if often called the
>"nifty-counter" technique, after a name used in the example.
>It is the technique often used for implementing iostreams,
>to ensure that the standard streams (cin, cout, cerr) are
>constructed before any objects that use them. It isn't
>foolproof, and programmers sometimes manage to create
>cross-dependencies that cause program failure during
>startup.
>In addition, the technique destroys program locality when
>a helper object is created in every module during startup.
>It causes your entire program to be paged in before the
>first statement of main can run. On really big programs,
>I"ve heard of startup taking a LOT of extra time.
This is one of the classic "we did it that way to support
dumb legacy linkers" problems. Modula 2 did it right. Modula 2
binders do a dependency sort at link time to determine the
correct initialization order. If the program contains a circular
dependency, linker errors occur. That's the right answer.
It's best to avoid implicit global initialization that depends
on specific ordering. It probably won't work right, and debug
facilities
for initialization time are usually inadequate.
John Nagle
One solution would be to have a global (file-level) synchronization object
that is locked everytime during the static initialization function.
so in the implementation file, instead of
T& T::Tobject()
{
static T t(args);
return t;
}
you have (in Windows)
CCriticalSection csGlobalFileLock;
T& T::Tobject()
{
CSingleLock csl(&csGlobalFileLock,true);// unlocks on d'tor
static T t(args);
return t;
}
The CCriticalSection is presumably a much lighter-weight object than the
class T so the increase in memory usage from having the global around if its
not used is not that large a concern. On the other hand, the fact that there
is synchronization locking occurring EVERY time someone calls instance may be
a performance issue.
A good solution in many cases is to call all necessary static initializers in
a controlled manner, either in a startup block that gets called before
spawning any threads, or with a common synchronization lock.
Perhaps, the best approach might be a slightly different.
CCriticalSection csGlobalFileLock;
Foo& Foo::Instance()
{
static std::auto_ptr<Foo> pStaticFoo;
if (pStaticFoo.get() == NULL)
{
CSingleLock csl(&csGlobalFileLock,true);
// only one thread at a time can be in this code
if (pStaticFoo.get() == NULL)// if still NULL then we are the first here
{
Foo* pStaticFoo = new Foo();
pStaticFoo = pStaticFoo;
}
}
return *pStaticFoo;
}
This has the advantages of
1) Thread safe initialization
2) Efficient access once the static object has been initialized.
Cost = 1 global synchronization object
Mark Borgerding
Email: Mark_Borgerding*REPLACE_THIS_WITH_AT_SIGN*stercomm.com
-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp Create Your Own Free Member Forum
> The only thing I can think of is to design the class so that it is
> harmless to run the constructor twice. Then, in the Init::Init function,
> the various objects that need early constructing can be explicitly
> constructed with:
>
> new (&cout) iostream(...);
>
> Of course, at some point, the system is going to call the constructor
> itself (unless cout is hacked by declaring it in an assembly language
> module--ugh!). But it is pretty easy to make the constructor notice that
> it has already been called, with a private bool that is set by the
> constructor. This relies upon the fact that memory is cleared before
> anything else runs.
Hm, there's still one thing I don't understand. How comes &cout has a
meaningful value before its constructor is called? And if it has, i.e.
sufficient memory for the object is provided at &cout, why can't I just
assign cout?
A colleague told me that there was no such problem when using a basic
type
like int or a pointer, allowing me to write
mycout = new ostream(...);
with mycout declared as an ostream*. This would suggest that for static
variables of the basic types the necessary space is allocated during
compilation or linking already, whereas for user-defined types this is
done at run-time. Is that true?
Is this up to the compiler writers or does the standard say something
about it?
Thanks for your patience,
Stephan
If we had been willing to swallow the drastic change of requiring the
parentheses, it would have been easy to supply this automatically for
programmer-created streams. Just augment basic_stream with operator()(),
which returns *this.
--
Pete Becker
Dinkumware, Ltd.
http://www.dinkumware.com
Is there anything in the standard that would prohibit making cin, cout
and cerr macros that expand to function calls that return iostream
references?
> Some systems allow you to create global objects in multiple
> modules, and the linker will discard duplicates. ... This
> is the best solution for global objects, but it isn't portable.
> Not all systems support the technique, and the way you get
> the result on supported systems varies.
What I don't understand is why the standards committee felt powerless to
insist that a linker be smart enough to support this technique. After
all, they imposed a Herculean set of demands on the compiler writers.
Why not also insist that, in an environment that support separate
compilation, there be a means for supporting this feature? So what if
the compiler writers also have to write a more modern linker? That
amounts to perhaps ten or twenty percent more code to write.
--
Ciao,
Paul
That's difficult for things like iostreams, because you have to make
sure that all base classes and data members (including ios, which is
virtual) have default constructors that can harmlessly be called
multiple times. One gotcha may be the initialization of virtual function
table pointers, which "move around" during the construction of objects
in multi-level class heirarchies.
First of all, & on a global object is always "meaningful", in that it's
just a memory address. It may not be meaningful as an iostream, but
placement new only expects a void*.
Second, the iostream classes don't support assignment. In fact most true
"objects", meaning data structures that represent actual things, rather
than merely values, don't support assignment or copying, because those
concepts are only meaningful for values.
There are some old implementations that use assignment on iostreams, or
on a derived class called something like iostream_withassign, as a way
of changing the streambuf that the stream points to. That function is
now called rdbuf instead.
> A colleague told me that there was no such problem when using a basic
> type
> like int or a pointer, allowing me to write
> mycout = new ostream(...);
> with mycout declared as an ostream*. This would suggest that for
> static
> variables of the basic types the necessary space is allocated during
> compilation or linking already, whereas for user-defined types this is
> done at run-time. Is that true?
No. All static object space is allocated at compile time. In this case,
mycout is a global object which takes (typically) four bytes, while the
actual ostream is a heap object created at run-time during
initialization. As soon as this heap object is created, its address is
stored in the static object. If you wrote:
ostream mycout(...);
then the entire ostream object would be a static object, allocated
memory at compile time.
It doesn't.
As it is static, it has been "zero initialized", which for a complex type
like cout, doesn't necessarily mean much. (It does mean that all sub-
parts have been zero initialized, and the implementation of iostream
make take advantage of this fact.)
> And if it has, i.e.
> sufficient memory for the object is provided at &cout, why can't I just
> assign cout?
Basically, because assignment isn't defined on the type of cout, only
construction. Sufficient memory doesn't necessarily mean valid contents,
either; even if the class supported assignment, it isn't generally true
that assignment will work if the target hasn't been correctly constructed.
> A colleague told me that there was no such problem when using a basic
> type
> like int or a pointer, allowing me to write
> mycout = new ostream(...);
> with mycout declared as an ostream*. This would suggest that for static
> variables of the basic types the necessary space is allocated during
> compilation or linking already, whereas for user-defined types this is
> done at run-time. Is that true?
No. Basically, if the type has a non-trivial constructor (and all user
defined constructors are considered non-trivial), then the allocation of
space and the construction of the object are two distinct operations.
And assignment to the object is only valid after construction. (There
are ways around this rule in specific situations, but they are only for
the expert.) The assignment to a pointer works because pointers don't
have non-trivial constructors.
> Is this up to the compiler writers or does the standard say something
> about it?
This is all defined in section 3.8.
--
James Kanze +33 (0)1 39 23 84 71 mailto: ka...@gabi-soft.fr
+49 (0)69 66 45 33 10 mailto: jka...@otelo.ibmmail.com
GABI Software, 22 rue Jacques-Lemercier, 78000 Versailles, France
Conseils en informatique orientée objet --
-- Beratung in objektorientierter Datenverarbeitung
-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp Create Your Own Free Member Forum
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
Constructors don't allocate memory for their object, they just initialize it
(of course the initialization may involve allocating memory for pointers or
references). In effect global objects get their memory before any code you
can write is executed.
>And if it has, i.e.
>sufficient memory for the object is provided at &cout, why can't I just
>assign cout?
Some assignments count on the destination being already initialized.
Imagine for a Vector class:
Vector& operator=(const Vector& that)
{
if(this == &that) return;
delete[] m_array; // undefined if m_array is uninitialized
m_array = Copy(that.m_array);
}
It is remotely possible that on your system you could initialize cout with
memcpy(&cout, some_other_memory, sizeof(cout));
but I wouldn't count on it (even if it worked with the current version of my
compiler).
A macro would have problems with
mynamespace::cout;
An alternative trick (that doesn't quite work) would be to have an object
that has a conversion to ostream&
class fake_ostream
{
public:
operator ostream&(){ return the_real_cout(); }
} cout;
but there are a number of situations where this behaves differently from a
real ostream.