couldn't understand the below section from TC++PL

G

goodbyeera

Before starting with my question, I now first type in the original
section from TC++PL as below:

21.5.2 Closing of Streams

A file can be explicitly closed by calling close() on its stream:

void f(ostream& mystream)
{
// ...
mystream.close();
}


However, this is implicitly done by the stream's destructor. So an
explicit call of close() is needed only if the file must be closed
before reaching the end of the scope in which its stream was
declared.

This raises the question of how an implementation can ensure that the
predefined streams cout, cin, cerr, and clog are created before their
first use and closed (only) after their last use. Naturally, different
implementations of the <iostream> stream library can use different
techniques to achieve this . After all, exactly how it is done is an
implementation detail that should not be visible to the user. Here, I
present just one technique that is general enough to be used to ensure
proper order of construction and destruction of global objects of a
variety of types. An implementation may be able to do better by taking
advantage of special features of a compiler or linker.

The fundamental idea is to define a helper class that is a counter
that keeps track of how many times <iostream> has been included in a
separately compiled source file:

class ios_base::Init {
static int count;
public:
Init();
~Init();
} ;

namespace { ios_base::Init __ioinit; } // in <iostream>, one copy in
eachfile #including <iostream>

int ios_base::Init::count = 0; // in some .c file

Each translation unit ($9.1) declares its own object called __ioinit.
The constructor for the __ioinit objects uses ios_base::Init::count as
a first-time switch to ensure that actual initialization of the global
objects of the stream I/O library is done exactly once:

ios_base::Init::Init() { if (count++ == 0) { /* initialize cout, cerr,
cin, etc. */ } }

Conversely, the destructor for the __ioinit objects uses
ios_base::Init::count as a last-time switch to ensure that the streams
are closed:

ios_base::Init::~Init() { if (--count == 0) { /* clean up cout (flush,
etc.) , cerr, cin, etc. */ } }

This is a general technique for dealing with libraries that require
initialization and cleanup of global objects. In a system in which all
code resides in main memory during execution, the technique is almost
free. When that is not the case, the overhead of bringing each object
file into main memory to execute its initialization function can be
noticeable. When possible, it is better to avoid global objects. For a
class in which each operation performs significant work, it can be
reasonable to test a first-time switch (like ios_base::Init::count) in
each operation to ensure initialization. However, that approach would
have been prohibitively expensive for streams. The overhead of a first-
time switch in the functions that read and write single characters
would have been quite noticeable.


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I don't understand very much about how the above technique "ensure
that the predefined streams cout, cin, cerr, and clog are created
before their first use and closed (only)
after their last use". My understanding to this quoted sentence is
that, we
need to make sure, when a global object uses cout in its constructor,
cout should already be constructed by the time; and when a global
object uses cout in its destructor, cout must not be destructed yet
by
the time. It seems that this can be achieved only by some
implementation-defined special technique, since the construction
sequence of global objects from different compilation unit is
unspecified.
Also, the text says that, in ios_base::Init::Init(), it will
initialize cout. But cout is of type basic_ostream<char>, and it has
only one form of constructor which takes a pointer to a
basic_streambuf, and doesn't have any member other function to re-
initialize
it after construction (while in the case for basic_ofstream, it can
be
constructed using a default constructor, and then be called with the
member function open() later to get initialized). So I don't
understand very much about what the "initialize cout" means in the
above text.
 
B

Bo Persson

Before starting with my question, I now first type in the original
section from TC++PL as below:

21.5.2 Closing of Streams [snip]

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I don't understand very much about how the above technique "ensure
that the predefined streams cout, cin, cerr, and clog are created
before their first use and closed (only)
after their last use". My understanding to this quoted sentence is
that, we
need to make sure, when a global object uses cout in its
constructor, cout should already be constructed by the time; and
when a global object uses cout in its destructor, cout must not be
destructed yet
by
the time. It seems that this can be achieved only by some
implementation-defined special technique, since the construction
sequence of global objects from different compilation unit is
unspecified.

Yes, but the implementation is allowed to define a partial order if
needed, using a special #pragma for example.

Before using cout or cin, you must include <iostream> which should
take care of the initialization. Perhaps it uses a special pragma, or
perhaps it uses the Init class presented in the book. Or some linker
magic. We don't have to know!

Also, the text says that, in ios_base::Init::Init(), it will
initialize cout. But cout is of type basic_ostream<char>, and it
has only one form of constructor which takes a pointer to a
basic_streambuf, and doesn't have any member other function to re-
initialize
it after construction (while in the case for basic_ofstream, it can
be
constructed using a default constructor, and then be called with the
member function open() later to get initialized). So I don't
understand very much about what the "initialize cout" means in the
above text.

Ok, so the special Init class constructor will also have to make sure
that a streambuf object is available (and possibly connected to
stdout) before it can "initialize cout". :)


The language is vague on purpose, because different systems handle
this in various ways. The language standard just tells us what should
happen, and not how to make it happen.



Bo Persson
 
A

AnonMail2005

Before starting with my question, I now first type in the original
section from TC++PL as below:

21.5.2 Closing of Streams

A file can be explicitly closed by calling close() on its stream:

void f(ostream& mystream)
{
    // ...
    mystream.close();

}

However, this is implicitly done by the stream's destructor. So an
explicit call of close() is needed only if the file must be closed
before reaching the end of the scope in which its stream was
declared.

This raises the question of how an implementation can ensure that the
predefined streams cout, cin, cerr, and clog are created before their
first use and closed (only) after their last use. Naturally, different
implementations of the <iostream> stream library can use different
techniques to achieve this . After all, exactly how it is done is an
implementation detail that should not be visible to the user. Here, I
present just one technique that is general enough to be used to ensure
proper order of construction and destruction of global objects of a
variety of types. An implementation may be able to do better by taking
advantage of special features of a compiler or linker.

The fundamental idea is to define a helper class that is a counter
that keeps track of how many times <iostream> has been included in a
separately compiled source file:

class ios_base::Init {
    static int count;
public:
    Init();
    ~Init();

} ;

namespace { ios_base::Init __ioinit; } // in <iostream>, one copy in
eachfile #including <iostream>

int ios_base::Init::count = 0; // in some .c file

Each translation unit ($9.1) declares its own object called __ioinit.
The constructor for the __ioinit objects uses ios_base::Init::count as
a first-time switch to ensure that actual initialization of the global
objects of the stream I/O library is done exactly once:

ios_base::Init::Init() { if (count++ == 0) { /* initialize cout, cerr,
cin, etc. */ } }

Conversely, the destructor for the __ioinit objects uses
ios_base::Init::count as a last-time switch to ensure that the streams
are closed:

ios_base::Init::~Init() { if (--count == 0) { /* clean up cout (flush,
etc.) , cerr, cin, etc. */ } }

This is a general technique for dealing with libraries that require
initialization and cleanup of global objects. In a system in which all
code resides in main memory during execution, the technique is almost
free. When that is not the case, the overhead of bringing each object
file into main memory to execute its initialization function can be
noticeable. When possible, it is better to avoid global objects. For a
class in which each operation performs significant work, it can be
reasonable to test a first-time switch (like ios_base::Init::count) in
each operation to ensure initialization. However, that approach would
have been prohibitively expensive for streams. The overhead of a first-
time switch in the functions that read and write single characters
would have been quite noticeable.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I don't understand very much about how the above technique "ensure
that the predefined streams cout, cin, cerr, and clog are created
before their first use and closed (only)
after their last use".  My understanding to this quoted sentence is
that, we
need to make sure, when a global object uses cout in its constructor,
cout should already be constructed by the time;  and when a global
object uses cout in its destructor, cout must not be destructed yet
by
the time.  It seems that this can be achieved only by some
implementation-defined special technique, since the construction
sequence of global objects from different compilation unit is
unspecified.
Also, the text says that, in ios_base::Init::Init(), it will
initialize cout.  But cout is of type basic_ostream<char>, and it has
only one form of constructor which takes a pointer to a
basic_streambuf, and doesn't have any member other function to re-
initialize
it after construction (while in the case for basic_ofstream, it can
be
constructed using a default constructor, and then be called with the
member function open() later to get initialized).  So I don't
understand very much about what the "initialize cout" means in the
above text.

For the order or initialization issue, this works because the
dependent item (the static int count variable) is initialized with a
constant expression and so does not required runtime initialization.

I found a blurb about this in the same book section, 9.4.1 (last
paragraph).

HTH
 
G

goodbyeera

Yes, but the implementation is allowed to define a partial order if
needed, using a special #pragma for example.

Before using cout or cin, you must include <iostream> which should
take care of the initialization. Perhaps it uses a special pragma, or
perhaps it uses the Init class presented in the book. Or some linker
magic. We don't have to know!

But look at what the author has said:"Here, I present just one
technique that is general enough to be used to ensure proper order of
construction and destruction of global objects of a variety of types.
An implementation may be able to do better by taking advantage of
special features of a compiler or linker." So it is natural to assume
that the technique the author presented in that section is meant to be
implementation-independent.
My understanding is that, having a global object of type
ios_base::Init defined in an anonymous namespace directly in the
<iostream> header will result in a file-scope global object in each
compilation unit that includes <iostream> (thus uses cout) and ensure
that the construction of each __ioinit happens earlier than the
construction of any other global objects in the corresponding
compilation unit, and the destruction happens later. But that has
nothing to do with the construction/destruction of the global object
cout defined in some other compilation unit. By the time when the
first __ioinit gets constructed, the cout object may not be
constructed yet at all. And the same problem for destruction too.
This is where my confusion is all about.
 
J

James Kanze

Before starting with my question, I now first type in the original
section from TC++PL as below:
21.5.2 Closing of Streams
A file can be explicitly closed by calling close() on its stream:
void f(ostream& mystream)
{
// ...
mystream.close();
}
However, this is implicitly done by the stream's destructor.
So an explicit call of close() is needed only if the file must
be closed before reaching the end of the scope in which its
stream was declared.

This is a questionable statement. Since close() can fail, you
need to test the state of the stream after close(), at least if
it is an output stream. Which effectively means that the close
in the destructor is only useful as a safeguard; typically, only
in case of an exception which will result in the data in the
file being ignored anyway.
This raises the question of how an implementation can ensure
that the predefined streams cout, cin, cerr, and clog are
created before their first use and closed (only) after their
last use. Naturally, different implementations of the
<iostream> stream library can use different techniques to
achieve this . After all, exactly how it is done is an
implementation detail that should not be visible to the user.
Here, I present just one technique that is general enough to
be used to ensure proper order of construction and destruction
of global objects of a variety of types. An implementation may
be able to do better by taking advantage of special features
of a compiler or linker.

[Tricky counter idiom elided...]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I don't understand very much about how the above technique
"ensure that the predefined streams cout, cin, cerr, and clog
are created before their first use and closed (only) after
their last use". My understanding to this quoted sentence is
that, we need to make sure, when a global object uses cout in
its constructor, cout should already be constructed by the
time; and when a global object uses cout in its destructor,
cout must not be destructed yet by the time.

Sort of. According to the (current) standard, construction is
not guaranteed unless you explicitly define an instance of
std::ios::Init before you define the object in question. The
classical iostreams required that <iostream.h> contain such an
instance, and the next version of the standard will require that
<iostream> contain one, so if you include one of these files
before your definition, you're OK. In practice, this isn't
always the case, however---in practice, it's actually fairly
rare to include <iostream>---so you're still stuck with being
careful. The general rule is that if a constructor is to output
to one of the standard streams, it also constructs an instance
of std::ios_base::Init first.

The standard guarantees that none of the standard streams will
ever be destructed, nor closed, unless you explicitly close
them. All that happens is when the tricky pointer drops to
zero, the stream is flushed. For the same reasons you construct
an instance of std::ios_base::Init in the constructor, you
explicitly flush all output in the destructor. (Note, too, that
if your program is outputting to std::cout, you'll want to flush
it explicitly before terminating, in order to return an error
code if there was a problem.)
It seems that this can be achieved only by some
implementation-defined special technique, since the
construction sequence of global objects from different
compilation unit is unspecified.

The trick is to force construction from the constructor of an
object defined in all relevant compilation units. It doesn't
really accomplish as much as is needed, however.
Also, the text says that, in ios_base::Init::Init(), it will
initialize cout. But cout is of type basic_ostream<char>, and
it has only one form of constructor which takes a pointer to a
basic_streambuf, and doesn't have any member other function to
re- initialize it after construction (while in the case for
basic_ofstream, it can be constructed using a default
constructor, and then be called with the member function
open() later to get initialized). So I don't understand very
much about what the "initialize cout" means in the above text.

It's up to the implementation to make it work. Technically, I
don't think it can be done within the scope of the standard, but
practically... in most cases, you can arrange for the object to
work correctly if everything is zero initialized, and provide a
special no-op constructor. Technically, a compiler could insert
code at the start of any constructor to overwrite just about
anything in the object, but in practice, none do for simpler
objects.

Of course, istream and ostream are not simple objects; in
particular, they have virtual functions, which means that the
compiler *will* overwrite some of their memory, even if the
constructor is empty. And making them work correctly with zero
initialization may entail considerable effort, with negative
repercusions on their performance. A more practical solution
for such objects is to somehow declare them as a simple array of
raw bytes. A char[] would do the trick, except that you need to
guarantee alignment; every assembler I've ever encountered
supports this very well too. And std::ios_base::Init will use
placement new to effectively call the constructor on the raw
memory.
 
J

James Kanze

[...]
Before using cout or cin, you must include <iostream> which
should take care of the initialization. Perhaps it uses a
special pragma, or perhaps it uses the Init class presented in
the book. Or some linker magic. We don't have to know!

Or perhaps it doesn't do anything. The standard doesn't require
anything here.

The next version of the standard will require that <iostream>
define an instance of std::ios::Init, ensuring that if a file
includes <iostream>, the standard stream objects will have been
constructed before any objects with namespace scope which follow
the include in the same file. But this doesn't really buy you
much:

User.cc

#include "SomeClass.hh"

SomeClass hasStaticLifetime ;

// ...

SomeClass.cc

#include <iostream>

SomeClass::SomeClass()
{
std::cout << ... ;
}

The include of <iostream> in SomeClass.cc will guarantee that
std::cout is constructed before any objects with namespace scope
in SomeClass.cc, but it makes no guarantees with respect to
objects in User.cc

Note too that the guarantee only applies to objects at namespace
scope. If you're using the singleton idiom, for example, the
actual "global" object will be at block scope or allocated with
new, and once again, you have no guarantee.

In sum, anytime you use one of the standard streams in a
constructor, you should also define an instance of
std::ios_base::Init, e.g.:

SomeClass::SomeClass()
{
static std::ios_base::Init dummyToControlInitialization ;
std::cout << ...
}

Similarly, the standard guarantees that the standard streams are
*never* destructed; the final decrementation in
std::ios_base::Init::~Init() only flushes them. But since the
same considerations that affect order of initialization affect
order of destruction, you should take care to flush the stream
anytime you output in a destructor. (This is usually taken care
of implicitly, by std::endl. And you should be using std::endl
systematically, unless you're sure that more output will
immediately follow. Which can't be the case for the final
output in a destructor.)

(This *should* be standard practice, and it *should* be
explained in any beginner's text. A lot of things that should
be aren't, however.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top