[comp.lang.c++] problems with calling streams from constructors

jbuck@galileo.berkeley.edu (Joe Buck) (08/16/90)

With g++ (1.37.1) and libg++ (1.37.0) the following code results in a fatal
error when the MyClass constructor is called for globalObj:

class MyClass {
	int i;
public:
	MyClass();
	// other stuff here
}

MyClass::MyClass () {
	cerr << "Calling constructor for MyClass\n";
	i = 0;
}

MyClass globalObj;

The error occurs because global objects are initialized in the order
that object modules are handed to the linker, and cerr is not yet
initialized when the constructor for globalObj is called.  As a result,
the ostream::operator<<(whatever) function dereferences null pointers.

I know I can work around this problem by searching libg++ twice; I
also have a workaround that causes gnulib3.c to call the constructors
in reverse order.  Reverse order always works when everything but main()
is obtained from a library and there are no circular dependencies.
Still, it's an inelegant hack.

Ellis and Stroustrup (p. 21) discuss this problem and imply that this
problem has been solved for the iostream library.  Can anyone discuss
how this is done?  I have similar problems in other code.  If there is
a standard fix, could it be implemented in libg++?  I hate having to
avoid streams because a function may be called by a constructor.


--
Joe Buck
jbuck@galileo.berkeley.edu	 {uunet,ucbvax}!galileo.berkeley.edu!jbuck	

kearns@cs.columbia.edu (Steve Kearns) (08/16/90)

In article <38170@ucbvax.BERKELEY.EDU> jbuck@galileo.berkeley.edu (Joe Buck) writes:
> .... problems with initializing classes ..............
>
>The error occurs because global objects are initialized in the order
>that object modules are handed to the linker, and cerr is not yet
>initialized when the constructor for globalObj is called.  As a result,
>the ostream::operator<<(whatever) function dereferences null pointers.
>
>I know I can work around this problem by searching libg++ twice; I
>also have a workaround that causes gnulib3.c to call the constructors
>in reverse order.  Reverse order always works when everything but main()
>is obtained from a library and there are no circular dependencies.
>Still, it's an inelegant hack.
>
>Ellis and Stroustrup (p. 21) discuss this problem and imply that this
>problem has been solved for the iostream library.  Can anyone discuss
>how this is done?  I have similar problems in other code.  If there is
>a standard fix, could it be implemented in libg++?  I hate having to
>avoid streams because a function may be called by a constructor.
>

Here is my solution, that works fine: The usual way to initialize a
class, say class Foo, is to declare a special constructor for Foo, say
Foo(int, char *), or a special friend class, say FooInit, and then
declare "static Foo myIniter(5, "fake");" or "static FooInit
myIniter;" in the class implementation file.  

In order to modify this system to initialize a class before it is
used, just put the "static Foo myIniter(5, "fake");" or "static
FooInit myIniter;" in the class HEADER (.h) file, instead, and make
sure the initialization routine is smart enough to handle being called
more than once.  Then any file that uses Foo.h will cause the
initialization routine to be called before any classes or code that
depends on Foo.  The only cost of this technique is a number of dummy
variables being declared, which can take up extra space.

One subtlety arises only in the following rare case: assume that
Foo.cc (the implementation file) includes .h files which use this
trick, but Foo.h does not include these .h files.  Then the Foo
constructor might be called before the classes Foo depends on are
initialized.  The only simple way around this problem is to make sure
that Foo.h includes every .h file that Foo.cc does.  In practice,
Foo.cc usually includes just Foo.h, which includes all other .h files;
therefore this issue rarely arises.

-steve

rfg@NCD.COM (Ron Guilmette) (08/16/90)

In article <38170@ucbvax.BERKELEY.EDU> jbuck@galileo.berkeley.edu (Joe Buck) writes:
<With g++ (1.37.1) and libg++ (1.37.0) the following code results in a fatal
<error when the MyClass constructor is called for globalObj:
<
[ ... code ommitted ... ]
<
<The error occurs because global objects are initialized in the order
<that object modules are handed to the linker, and cerr is not yet
<initialized when the constructor for globalObj is called.  As a result,
<the ostream::operator<<(whatever) function dereferences null pointers.
<
<I know I can work around this problem by searching libg++ twice; I
<also have a workaround that causes gnulib3.c to call the constructors
<in reverse order.  Reverse order always works when everything but main()
<is obtained from a library and there are no circular dependencies.

If there are circular dependencies, you are in big trouble no matter which
order the initializations get done in!

Also, you seem to be suggesting that there will be some problem(s) with
this nice simple approach if you do not pull everything except main()
from some library.  I don't see why you are worried about it.  It can work
fine even if you are linking some arbitrary collection of .o files and .a
files in some arbitrary order (as long as your linking order is
consistant with the partial ordering imposed by the initialization
dependencies).

<Still, it's an inelegant hack.

What makes you think so?  I happen to believe the exact opposite, i.e.
that it is elegant precisely because it works, it is simple, and it
still allows the user the user a maximal level of control (via specifying
some desired linking order).

Do you have a better solution?
-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

vaughan@mcc.com (Paul Vaughan) (08/17/90)

Here is an idea for assuring static object intialization prior to use
that can work for streams implementation (particularly libg++).  I've
done this sort of thing for my own classes occasionally, and I think I
may have suggested it to Doug Lea for libg++, but I'm not sure.  The
code here is oriented toward the libg++ stream implementation.


One way it might be done is this.  Toward the end
of stream.cc, insert

//#include <stream.h>  //add this so this fragment will actually compile

class CoutHolder {
public:	
  ostream* coutp;
  CoutHolder() : coutp(0) {};
  ~CoutHolder() { delete coutp; }
};

static CoutHolder coutHolder; //will provide for destruction of cout.

extern ostream* _coutp_;
ostream* _coutp_ = _coutp_ ? (coutHolder.coutp = _coutp_ ) 
     : (coutHolder.coutp =  new ostream(3));
  // fill in more appropriate args for the ostream constructor.	

//-----------------------
// In stream.h

extern ostream* _coutp_;
static ostream& mycout = _coutp_ ? *_coutp_ : *(_coutp_ = new ostream(3));
//mycout should be named cout--I renamed it so this would compile.


The point of the CoutHolder is to get the cout stream destructed as it
currently is.  This is the enabling rule: The language guarantees you
that any external global variables (such as the ostream& _coutp_) will
either have been initialized or will be 0 during module intialization,
regardless of the order.  If the stream.o module gets intialized
first, it will find _coutp_ = 0 and will allocate a new one, recording
it in the holder for later destruction.  Then other modules will find
_coutp_ already initialized and will just keep the one that's aready
there.

But if some other module is intialized first, it will find that
_coutp_ = 0 and so it will allocate a new one and record it in the
_coutp_ variable.  Then when the stream.o module is initialized, it
will find _coutp_ !=0 and so will not make a new one but will record
the existing one in the holder for later destruction.  



Notes:

If you could count on the address of an unitialized reference being 0,
you might be able to eliminate the _coutp_ global variable.  

As I've written it, you couldn't include stream.h in stream.cc, which
is kind of unhandy.  You might protect the stuff that I'm adding to
stream.h with a #ifndef and define a symbol in stream.cc so that those
lines wouldn't get into stream.cc.


Since the CoutHolder class definition doesn't go into a .h file,
nobody else sees it, so it doesn't clutter things up.

Of course, stream.h must be protected against multiple inclusions, but
I'm sure it already is.

cout must be static, because it will be declared and initialized in
many different modules.  This has the interesting and possibly
undesirable property that cfront will warn if you include stream.h but
then don't use cout.  There's probably a simple way to get around that
though.

I've used this sort of technique before for intializing lists of
objects created at the file scope in different modules.

 Paul Vaughan, MCC CAD Program | ARPA: vaughan@mcc.com | Phone: [512] 338-3639
 Box 200195, Austin, TX 78720  | UUCP: ...!cs.utexas.edu!milano!cadillac!vaughan