I have a problem with global objects in a static library. I'm using Visual
Studio .NET 2003.
For Example:
I have a class called CTest which is defined in the files test.h and
test.cpp. This files are part of a static library project. If the
constructor of CTest is called, the object is register it self in a global
list. In test.cpp also global objects of CTest are instantiated. That means
at the end of test.cpp are the follwing lines:
CTest oTest("object 1");
CTest oTest("object 2");
Later this global objects can be found in the global list by name and are
used in some other objects int the static library.But the constructors of
this objects are never called.
If test.h and test.cpp are part of the program project it works but not if
they are part of the static library.What is going wrong? Is this a known
problem with Visual C++? How can it be solved?
Btw. if I instantiate an object of CTest (e.g. CTest oTest("Dummy");) in an
other cpp file of the static library the constructors of all CTest objects
are called, also the constructors of the objects defined in test.cpp. But
this is not really a good solution. The other cpp file of the static library
defines an object which is used by the program directly.
What does the constructor do and how do you know it isn't called?
I think that the constructor has to be called. My guess would be that it is
not being called in the order that you expect (relative to other
constructors) and that this is causing misbehaviour. The order of
initialisation of global objects in different files is not defined by the
language --- it can be anything.
--
John Carson
1. To reply to email address, remove donald
2. Don't reply to email address (post here instead)
1) Debugger don't stop on a breakpoint in the constructor
2) The global list is empty, or more exactly, the pointer to the global list
is 0 because the list is first created before the first object is added to
it.
> I think that the constructor has to be called.
Yes. It think too. And I think Bjarne Stroustrup think this also. At least
he wrote this in the book "The C++ programming language". ;-) At least as
far I understood this.
> My guess would be that it is not being called in the order that you expect
(relative to other
> constructors) and that this is causing misbehaviour. The order of
> initialisation of global objects in different files is not defined by the
> language --- it can be anything.
>
As I wrote, all is working if I have it in the program project itselfs. But,
for some reasons, we moved this part in a own sub project, a static library.
I know, that the order of initilisation of global objects in different files
is not defined, but this is not the problem. Trust me, the contructors of
the objects are definitivly not called.
As I wrote before, if I initialize a global object in an other cpp file
(which defines a class used in the program directly) the constructor is
called. Not only the constructor in this file, but also the before not
called constructos in the other file (test.cpp in my example).
[snip]
> As I wrote, all is working if I have it in the program project
> itselfs. But, for some reasons, we moved this part in a own sub
> project, a static library.
That is what made me suspect that it is an order of initialisation issue. I
have had code not work when in a static library yet work in the main program
simply because the change affected the order of initialisation.
> As I wrote before, if I initialize a global object in an other cpp
> file (which defines a class used in the program directly) the
> constructor is called. Not only the constructor in this file, but
> also the before not called constructos in the other file (test.cpp in
> my example).
One question. When all constructors get called, have you verified this with
a breakpoint in the constructor or just with the global list? I ask this
because of the possibility that there may be some IDE setup issue involved
with the debugger (and because, confronted with a perplexing problem, all
possibilities need to be explored).
As far as I know, the only way for a constructor to not be called is if the
constructor doesn't do anything and the constructor call is optimised away.
This, however, shouldn't be an issue with a debug version.
Basically there only seem two possibilities: there is a bug in the compiler
or a bug in your code. I suspect that the only way to identify either may be
with a compileable example that exhibits the problem.
>Hello,
>
>I have a problem with global objects in a static library. I'm using Visual
>Studio .NET 2003.
>
>For Example:
>I have a class called CTest which is defined in the files test.h and
>test.cpp. This files are part of a static library project. If the
>constructor of CTest is called, the object is register it self in a global
>list. In test.cpp also global objects of CTest are instantiated. That means
>at the end of test.cpp are the follwing lines:
>
>CTest oTest("object 1");
>CTest oTest("object 2");
>
>Later this global objects can be found in the global list by name and are
>used in some other objects int the static library.But the constructors of
>this objects are never called.
It can be dangerous to rely on constructors for global objects that
aren't explicitly accessed being executed before main starts - they
aren't guaranteed to by the standard, and you've come across an
instance when they aren't.
>If test.h and test.cpp are part of the program project it works but not if
>they are part of the static library.What is going wrong? Is this a known
>problem with Visual C++? How can it be solved?
It is a known feature, rather than problem. If you want to avoid
having unreferenced symbols, such as "unused" globals, eliminated then
you should link with /OPT:NOREF, or better, leave it as /OPT:REF and
add /INCLUDE:oTest1,oTest2,oTest3 to your command line. I think you
want the options in your .exe project rather than the .lib, as long as
the globals in question have external linkage (i.e. aren't static).
>Btw. if I instantiate an object of CTest (e.g. CTest oTest("Dummy");) in an
>other cpp file of the static library the constructors of all CTest objects
>are called, also the constructors of the objects defined in test.cpp. But
>this is not really a good solution. The other cpp file of the static library
>defines an object which is used by the program directly.
This is because by referencing CTest in another unit that is
referenced, you are bringing in the whole .obj file that CTest is
defined in into your .lib file (the linker isn't very clever), and
this includes the global objects you've defined there.
Tom
--
C++ FAQ: http://www.parashift.com/c++-faq-lite/
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Sure, I checked this with a breakpoint.
I now generated a map file by the linker. The linker didn't include the
objects. They are not listed in the map file. Only if I initialize this
"Dummy-Object" the Objects/Classes are also listed in the map file.
> >If test.h and test.cpp are part of the program project it works but not
if
> >they are part of the static library.What is going wrong? Is this a known
> >problem with Visual C++? How can it be solved?
>
> It is a known feature, rather than problem. If you want to avoid
> having unreferenced symbols, such as "unused" globals, eliminated then
> you should link with /OPT:NOREF, or better, leave it as /OPT:REF and
> add /INCLUDE:oTest1,oTest2,oTest3 to your command line. I think you
> want the options in your .exe project rather than the .lib, as long as
> the globals in question have external linkage (i.e. aren't static).
>
The globals are static, because they are only used internally in the static
library, and not direct by the program. /OPT:NOREF didn't work, I already
try this. I will try the /INCLUDE option. You wrote oTest1 as the object
name? What must be given to the INCLUDE option? The name of the object file,
in my example test.o, or the name of the class, or ... ?
Can I use the INCLUDE option in the static library too, even it is not the
linker option? I think its the option for the libtool (I have a german
Visual Studio, its called Bibliothekar here).
> >Btw. if I instantiate an object of CTest (e.g. CTest oTest("Dummy");) in
an
> >other cpp file of the static library the constructors of all CTest
objects
> >are called, also the constructors of the objects defined in test.cpp. But
> >this is not really a good solution. The other cpp file of the static
library
> >defines an object which is used by the program directly.
>
> This is because by referencing CTest in another unit that is
> referenced, you are bringing in the whole .obj file that CTest is
> defined in into your .lib file (the linker isn't very clever), and
> this includes the global objects you've defined there.
>
Yes I have checked this with the linkers map file. The linker just ignores
the object file if no "Dummy-Object" is generated.
The standard guaratees that file-scoped variables are constructed before any
other code in the same translation unit is executed. Since in your case no
other code in the translation unit will ever run unless the constructor runs
first, the compiler is within it's rights under the standard to simply omit
the entire translation unit from the executable. This is a common problem
with "virtual constructor" patterns when the classes are placed in a static
library.
> The globals are static, because they are only used internally in the
> static library, and not direct by the program. /OPT:NOREF didn't
> work, I already try this. I will try the /INCLUDE option. You wrote
> oTest1 as the object name? What must be given to the INCLUDE option?
> The name of the object file, in my example test.o, or the name of the
> class, or ... ?
The argument to /INCLUDE is a symbol name. In the case of a C++ object, it
has to be the "decorated" (or "mangled") name of the object in question.
>
> Can I use the INCLUDE option in the static library too, even it is
> not the linker option? I think its the option for the libtool (I have
> a german Visual Studio, its called Bibliothekar here).
You could put the /INCLUDE directive in the library, but it wouldn't help.
In general, the main translation unit (the one containing main or DllMain or
WinMain or ...) needs to force a reference to all the modules that it wants
to include. One way to do this is to have each class that is to use your
virtual constructor idiom supply a header file that contains at least the
following:
#pragma comment(lib,"theLibThatcontainsThisClass.lib")
#pragma
comment(linker,"/INCLUDE:\"the_mangled_name_of_a_symbol_in_this_module\"")
Then #include all of these files in your main translation unit (after all,
you do want to choose which virtually constructable classes are actually
included in your program, don't you?).
You can determine the decorated name of a symbol by compiling your virtually
constructable class, then using dumpbin /symbols myclass.obj and searching
for the name that you want to reference (for example, to find the mangled
name for CMyClass::CMyClass, just search the output of dumpbin /symbols for
CMyTest::CMyTest - you'll see the mangled name to the left of the unmangled
name on the same line of dumpbin output).
-cd
>> It can be dangerous to rely on constructors for global objects that
>> aren't explicitly accessed being executed before main starts - they
>> aren't guaranteed to by the standard, and you've come across an
>> instance when they aren't.
>>
>Very interesting. I thought it was guaranteed by the standard, that all
>global objects are initialized before main is called. That means that the
>standard don't guarantee that global objects which aren't accesed directly,
>as described in my example, are created, even if they do something in the
>constructor? Only if the are accessed directly it is guaranteed? I must
>remember this for the future.
Strictly speaking, a global doesn't have to be initialized until a
function defined in the same translation unit is called. This fits in
with the problem you were having with the .lib file, and I think it's
there to allow lazy implementations of dynamic linking (where the
dynamic link library is only loaded when you first call a function
from it).
>The globals are static, because they are only used internally in the static
>library, and not direct by the program.
I think this will make it impossible to reference them in an INCLUDE
linker option, since you can probably only name externally visible
names.
/OPT:NOREF didn't work, I already
>try this. I will try the /INCLUDE option. You wrote oTest1 as the object
>name? What must be given to the INCLUDE option? The name of the object file,
>in my example test.o, or the name of the class, or ... ?
The name of the object - basically, the symbol that you want to be
included. However, with C++ name mangling, you'll have to work out
what the external name of your variable is before you can include it!
>Can I use the INCLUDE option in the static library too, even it is not the
>linker option? I think its the option for the libtool (I have a german
>Visual Studio, its called Bibliothekar here).
If you name the object correctly, it works. e.g. for
extern "C"
{
int test = (std::cout << "Test init\n", 0);
}
worked (i.e. "Test init" was displayed) in a static library only when
I added /INCLUDE:"_test" to the linker options of the .exe.
Annoyingly, the safest technique is to just give the .lib file an
"init" function and call it from the start of main. That's what I'd go
with.
3FE 00000000 UNDEF notype External |
__imp_??0ErrorHandler@xercesc_2_5@@QAE@XZ (__declspec(dllimport) public:
__thiscall xercesc_2_5::ErrorHandler::ErrorHandler(void))
It shows (UNDEF) that Xalan is using Xerces' ErrorHandler constructor, but
this constructor in not linked into Xerces.lib, I've checked this using
dumpbin.exe. What should I do? How can I force Xerces.lib to include
ErrorHandler constructor/destructor?
Thank you in advance.
As someone else recommended, Xalan/Xerces community is more likely to have a
solution for you.
Is the constructor defind inline in the header file? If so, it's odd that
there would be a symbolic reference to it. Are you quite certain that the
version of Xalan you have was compiled against the version of Xerces you
have? (i.e. did you compile them yourself?)
-cd
> Are you quite certain that the
> version of Xalan you have was compiled against the version of Xerces you
> have? (i.e. did you compile them yourself?)
Yes, I've compiled both sources.
Thank you.
I've solved this problem. I've moved constructor body from .hpp file into
.cpp file. And added "/INCLUDE" as command line parameter into my project. I
have only one linker error, now. I've posted it today as separate topic
under title "inline".
Best regards.