[gnu.g++.lib.bug] 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.