Forcing global constructors to execute

Started by
21 comments, last by doynax 19 years, 5 months ago
Most good compilers optimizes away static constructors that are never used, consider:


struct Test
{
  Test(void)
  {
    printf("Constructor!\n");
  }
};

static Test test;

void
main(void)
{
  printf("Hello world!\n");
}


The text "Constructor" is never called. The compiler recoons that the object test is never used and thus there's no need to call it's constructor. I know this has been discussed before but I've still haven't found any "good" solutions to the problem. The only way that works is to actually use the object, this seems simple enough, but it makes the code ugglier since some other compilation unit must know about the object. You also need to do something valid with the object, the following won't work for instance:


...

void
main(void)
{
  Test* data = &test;
  printf("Hello world!\n");
}


Since data isn't used, it's not needed and thus test isn't needed either. This should work:


...

void
main(void)
{
  printf("Hello world! %d\n", int(&test));
}


Anyone got any good ideas/tricks?
Advertisement
When I think about it, isn't this behavior a bit odd?

I mean if my constructor increases some variable, and later that variable is used. Shouldn't the compiler realize that the constructor modifies a value that IS used, even though the object itself isn't?

It's fine to remove the test-object in this case:

struct Test{  Test(void)  {    m_data = 10;  }  int m_data;};static Test test;voidmain(void){  printf("Hello world!\n");}


While not in this case:

static int globalCount = 0;struct Test{  Test(void)  {    globalCount += 10;  }};static Test test;voidmain(void){  printf("Hello world! %d\n", globalCount);}


Or?
Quote:Original post by eq
Most good compilers optimizes away static constructors that are never used, consider:

*** Source Snippet Removed ***

The text "Constructor" is never called. The compiler recoons that the object test is never used and thus there's no need to call it's constructor.


Actually, Test's constructor should be getting called, you just aren't seeing anything printed to the screen. Non-trivial constructors are always going to be called, except in cases of a few standard optimizations.
Quote:Original post by eq
Most good compilers optimizes away static constructors that are never used.

No they don't. Can you give an example of where you've actually seen this behavior?
I'm currently using Intel C++ compiler v8.1. But I've had the same trouble with other compilers aswell. Currently I'm refactoring some Image-classes, basicly I register a bunch of sub-classes to my Image class, like this:

[Source]// ======== ImageA8.h ============class ImageA8 : public Image{    static const ImageFormat m_format;};[/Source]

[Source]// ======== ImageA8.cpp ============const Image::ImageFormat ImageA8::m_format(false, 0, 0, 0, 8, 0, &ImageA8::create);[/Source]


The ImageFormat constructor are never executed.

All code are present in a lib: Image.lib if that's causing problems. The constructor basicly adds the information about the ImageA8 class to a static linked list. Then when I need an image I just call Image::create with the desired format and an Image object of the closest matching format is returned.

Another place where this is desired is for registering file-types: image-loades, mesh-loaders etc.
Serializers might want this for registering object-names etc.

Quote:Original post by eq
The ImageFormat constructor are never executed.

All code are present in a lib: Image.lib if that's causing problems. The constructor basicly adds the information about the ImageA8 class to a static linked list. Then when I need an image I just call Image::create with the desired format and an Image object of the closest matching format is returned.


Your constructor is probably getting called. Your problem is most-likely attributed to the fact that you are relying on an implementation order of construction. Object construction order is not defined when you are working with multiple translation units (cpp files). You are only guaranteed that objects are constructed in the order they appear in each translation unit, however, the order in which objects from two different translation units are constructed is uncontrollable.

You are saying that the constructor of your object adds itself to a static linked list object, so what's most-likely happening is your constructor for the object is getting called before the constructor for the linked list (so the results of you attempting to add the object to the list are undefined). What's probably happening is the operation doesn't cause error, but the consructor resets the container's state, probably also causing a memory leak (this would be true if the implementation of the linked list has pointer datamembers and the operation to add an item to the list just dynamically allocates a node and assigns its address to the value, since this would work fine with uninitalized data, but when the constructor for the list gets called, it would just set the pointer to 0, causing a memory leak and the appearance of the object having never been added to the list).

A common solution to this is to us a static object inside of a functions, since static objects in functions are initialized on first use. If you need well-defined order of destruction, this solution doesn't work either, so you may be better off working with reference counting if that's the case.
Hmm. It's only guaranteed to be constructed at some point before first-use. So even though you call the constructor, unless you actually /use/ the instance, I don't think you can assume it's constructed. And as you pointed out, it's uglier.

This is a common problem for custom memory-allocators, for example, where you need to make sure the memory-manager is the first thing constructed because other globals need to allocate from it. However, because they're all in different compilation units and libs, it's a real big huge pain.

No the constructor is NOT called. I can put an __asm int 3; and there's no break. My test cases are obvisouly flaved, but here's a project that breaks: http://hem.bredband.net/b295424/test.zip (BTW how do you post links?)

It's basicly one lib with 2 classes, and a main console project.

I think the problem arises becuase the main project doens't do anything with the TestA8 class at all. If I make the TestA8::m_format public and recreates main.cpp as this, it all works:
#include "../lib/Test.h"#include "../lib/TestA8.h"voidmain(void){	Test* a = Test::create(0); // Should return 0	Test* b = Test::create(8); // Should return a pointer to a TestA8 object	const Test::TestFormat* c = &TestA8::m_format;}


This is basicly what I want to avoid: the main app shouldn't need to know all classes in the lib, just the main one.
Interseting thing:

MSVC6 in debug builds - works.
MSVC6 in release builds - failes.
Intel C++ 8.1 debug and release builds - failes.

This topic is closed to new replies.

Advertisement