C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

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

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language (comp.lang.c++)
View previous topic :: View next topic  
Author Message
Qin Chen
Guest





PostPosted: Thu Feb 26, 2004 2:50 pm    Post subject: Static initialization dependency -- Think in C++ 2nd Reply with quote



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 < 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::ofstream 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::ofstream 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 // Static initialization will force
// all these values to zero:
std::ofstream 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;
} ///:~
------------------------------------------------------------------------


Back to top
Tom
Guest





PostPosted: Mon Mar 01, 2004 12:01 pm    Post subject: Re: Static initialization dependency -- Think in C++ 2nd Reply with quote



"Qin Chen" <withstand (AT) etang (DOT) com> wrote

Quote:
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 < 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.

Quote:

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...

Quote:

Here in the code following, the static objects is std::ofstream 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.

Quote:

----------------------------------------------------------------------------
----
//: C10:Initializer.h
// Static initialization technique
#ifndef INITIALIZER_H
#define INITIALIZER_H
#include <iostream
#include

For my "portable" solution you need
extern std::ofstream* const out_pointer;
static std::ofstream& out = *out_pointer; //dynamic initialization

Quote:

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 #include 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::ofstream("hello.txt");

Quote:
out.open("hello.txt"); // program stuck here......

Now you don't need that.

Quote:
///////////////////////////////////////////////////////////////////
// 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::ofstream ofs;
out_pointer->~ofs();

Quote:
// 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::ofstream out;

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

#include
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::ofstream) /
sizeof(aligner);
std::size_t const aligner_count = sizeof(std::ofstream) %
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::ofstream* const out_pointer =
reinterpret_cast<std::ofstream*>(out_storage);


Quote:
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

Back to top
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language (comp.lang.c++) All times are GMT
Page 1 of 1

 
 


Powered by phpBB © 2001, 2006 phpBB Group