Static initialization dependency -- Think in C++ 2nd

Q

Qin Chen

I will present very long code, hope someone will read it all, and teach me
something like tom_usenet.

This question comes to me when i read <<Think in C++>> 2nd, chapter 10 ,
name control, section "Static initialization dependency". There is a
example to show how to solve the prolem involved with a technique first
poineered by Jerry Schwarz while creating the iostream library (because the
definitions for cin, cout, and cerr are static and live in a separate file).

The idea is to use a additional class responsible for the dynamic
initialization of your library¡¯s static objects.

Here in the code following, the static objects is std::eek:fstream out,
intended to be used as a log file maybe, and the addition class is
Initializer. In class Initializer's constructor, out initialize the log
file... but code stop there with a access voilation exception.......

I'm eager to know why....

----------------------------------------------------------------------------
----
//: C10:Initializer.h
// Static initialization technique
#ifndef INITIALIZER_H
#define INITIALIZER_H
#include <iostream>
#include <fstream>
extern std::eek:fstream out;

class Initializer {
static int initCount;
public:
Initializer();
~Initializer();
};
// The following creates one object in each
// file where Initializer.h is included, but that
// object is only visible within that file:
static Initializer init;
#endif // INITIALIZER_H ///:~
-----------------------------------------------------------------------

//Initializer.cpp
#include "Initializer.h"
#include <fstream>
#include <iostream>
Initializer::Initializer(){
// Initialize first time only
if(initCount++ == 0) {
std::cout << out.is_open() << std::endl; //return 0
// to test if out is good to work with
///////////////////////////////////////////////////////////////////
out.open("hello.txt"); // program stuck here......
///////////////////////////////////////////////////////////////////
// debug give infomation like this :
//First-chance exception in test.exe: 0xC0000005: Access Violation.
// i use VC++ 6.0.
}
}
Initializer::~Initializer()
{
std::cout << "~Initializer()" << std::endl;
// Clean up last time only
if(--initCount == 0) {
out.close();
// Any necessary cleanup here
}
}
------------------------------------------------------------------------
//: C10:InitializerDefs.cpp {O}
// Definitions for Initializer.h
#include "Initializer.h"
#include <fstream>
// Static initialization will force
// all these values to zero:
std::eek:fstream out;
int Initializer::initCount;
///:~
------------------------------------------------------------------------
// test file......
//: C10:Initializer2.cpp
//{L} InitializerDefs Initializer
// Static initialization
#include "Initializer.h"
using namespace std;

int main() {
out << "hello" << endl;
return 0;
} ///:~
------------------------------------------------------------------------
 
T

Tom

Qin Chen said:
I will present very long code, hope someone will read it all, and teach me
something like tom_usenet.

This question comes to me when i read <<Think in C++>> 2nd, chapter 10 ,
name control, section "Static initialization dependency". There is a
example to show how to solve the prolem involved with a technique first
poineered by Jerry Schwarz while creating the iostream library (because the
definitions for cin, cout, and cerr are static and live in a separate file).

I just downloaded the book and had a look, and unfortunately the
solution he proposes only works for POD objects. For non-PODs, the
details are somewhat different, and worse, there is no completely
portable way to do it.
The idea is to use a additional class responsible for the dynamic
initialization of your library¡¯s static objects.

Right, however, you need to use some platform specific trickery to
initialize the ostream. Read on...
Here in the code following, the static objects is std::eek:fstream out,
intended to be used as a log file maybe, and the addition class is
Initializer. In class Initializer's constructor, out initialize the log
file... but code stop there with a access voilation exception.......

I'm eager to know why....

It's because "out" hasn't been constructed by the time your
Initializer object attempts to access it (calling "open"). The order
that global objects from different translation units are initialized
in is not well defined - this is the infamous "static initialization
dependency fiasco". There are a number of ways of solving it, all of
which depend on some level of platform specific behaviour or compiler
extensions. I'll present a mostly portable version.

Incidently, one non-portable solution, for MSVC, is:
Add the following to InitializerDefs.cpp, just before the "out"
definition:
#pragma init_seg(compiler)

That forces the compiler to initialize objects declared in that
segment before initializing normal globals. Read on for a more
portable solution.
----------------------------------------------------------------------------
----
//: C10:Initializer.h
// Static initialization technique
#ifndef INITIALIZER_H
#define INITIALIZER_H
#include <iostream>
#include <fstream>

For my "portable" solution you need
extern std::eek:fstream* const out_pointer;
static std::eek:fstream& out = *out_pointer; //dynamic initialization
class Initializer {
static int initCount;
public:
Initializer();
~Initializer();
};
// The following creates one object in each
// file where Initializer.h is included, but that
// object is only visible within that file:
static Initializer init;
#endif // INITIALIZER_H ///:~
-----------------------------------------------------------------------

//Initializer.cpp
#include "Initializer.h"
#include <fstream>
#include <iostream>
Initializer::Initializer(){
// Initialize first time only
if(initCount++ == 0) {
std::cout << out.is_open() << std::endl; //return 0
// to test if out is good to work with
///////////////////////////////////////////////////////////////////

Here you should construct the stream in the storage that the reference
points to:
new (out_pointer) std::eek:fstream("hello.txt");
out.open("hello.txt"); // program stuck here......

Now you don't need that.
///////////////////////////////////////////////////////////////////
// debug give infomation like this :
//First-chance exception in test.exe: 0xC0000005: Access Violation.
// i use VC++ 6.0.
}
}
Initializer::~Initializer()
{
std::cout << "~Initializer()" << std::endl;
// Clean up last time only
if(--initCount == 0) {
out.close();

Here you need to destroy the stream:
typedef std::eek:fstream ofs;
out_pointer->~ofs();
// Any necessary cleanup here
}
}
------------------------------------------------------------------------
//: C10:InitializerDefs.cpp {O}
// Definitions for Initializer.h
#include "Initializer.h"
#include <fstream>
// Static initialization will force
// all these values to zero:
std::eek:fstream out;

Here's where you do the "portable" hack:

#include <cstddef>

namespace
{

union aligner
{
long double v1;
double v2;
long v3;
void (*v4)();
class dummy;
void (dummy::*v5);
void (dummy::*v6)();
void* v7;
};

std::size_t const temp_aligner_count = sizeof(std::eek:fstream) /
sizeof(aligner);
std::size_t const aligner_count = sizeof(std::eek:fstream) %
sizeof(aligner) ?
temp_aligner_count + 1 : temp_aligner_count;

//this is enough storage to fit the stream in:
aligner out_storage[aligner_count]; //hope this is properly aligned!

} //close anon namespace

//here's more "trickery". This is static initialization, hence carried
out
//before program start (and before init's etc. get initialized.
std::eek:fstream* const out_pointer =
int Initializer::initCount;
///:~
------------------------------------------------------------------------
// test file......
//: C10:Initializer2.cpp
//{L} InitializerDefs Initializer
// Static initialization
#include "Initializer.h"
using namespace std;

int main() {
out << "hello" << endl;
return 0;
} ///:~

With those changes, the above should work fine.

Tom
 

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