[gnu.g++] Construction of external objects

jac@sundance.llnl.gov (James Crotinger) (09/19/90)

  I have a question concerning the use of libraries in C++. Suppose I'm
trying to write a library like iostream.h. The library instantiates
cout and iostream.h contains the line:

	extern iostream_whatever cout;

[I'm grossly simplifying that, but you get the point.] 

Now the actual declaration of cout is in libiostream.a. My question is,
does cout get initialized.

On the Sun, the answer would apparently be "no". 

A short example is helpful. Consider the following class:

    // B.h

    #include <stdio.h>

    class B {

     public:
      B(char *str) { printf(str); printf("\n"); }

    };

and the "library" implementation file:

    // B.cc

    #include "B.h"

    B foobar("B.c");

and the main program:

    // main.cc

    #include <stdio.h>
    #include "B.h"

    extern B foobar;  // should really go in B.h

    main()
    {
      printf("In main()\n");

      B foo("main()");
    }

Now, if I compile this with the following line:

    CC -o test main.cc B.cc

then things work as expected and the following output is generated:

    B.c
    In main()
    main()

Now if I instead build a library and link to it:

    ar rc libfoo.a B.o
    ranlib libfoo.a
    CC -o test main.cc -L. -lfoo

I only get:

    In main()
    main()

i.e. the constructor for foobar is never called.

[note that g++ gives the same behavior]

Examination of the CC script which comes with Sun C++ shows why this
is true. They use the UNIX "nm" utility to find the names of the
static constructors; e.g.:

    CC -o test main.o -L. -lfoo
    nm test | munch > tmp.cc
    CC -o test main.o tmp.cc -L. -lfoo

The probem is that nm is only run on the executable and not on the 
libraries. Since the linker didn't see any calls to the __st[id]__.*
functions which were to initialize foobar, it did not load that function
into the executable and thus nm/munch can't find it and so it doesn't 
get called.

The question is: is this broken behavior? It is apparent from the
implementation of iostream.h that this behavior is known and expected.
They handle initialization of cout, etc., by declaring a static
instance of class Iostream_init() which apparently initializes the
standard streams when the first instance of Iostream_init is created
and destroys them when the last instance of Iostream_init is
destroyed.

This seems like an unnecessary kludge. Why not run the constructors
for all of the global objects declared in a library? Granted it would
be better to run them for only the objects which you use, but lacking
that it would seem to be less of a hack to just run them all, and keep
the sizes of you libraries small.

  Jim

--
-----------------------------------------------------------------------------
James A. Crotinger   Lawrence Livermore Nat'l Lab // The above views 
jac@gandalf.llnl.gov P.O. Box 808;  L-630     \\ // are mine and are not 
(415) 422-0259       Livermore CA  94550       \\/ necessarily those of LLNL.