Sign in to follow this  
eq

Forcing global constructors to execute

Recommended Posts

eq    654
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?

Share this post


Link to post
Share on other sites
eq    654
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;

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



While not in this case:



static int globalCount = 0;

struct Test
{
Test(void)
{
globalCount += 10;
}
};

static Test test;

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



Or?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sneftel    1788
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?

Share this post


Link to post
Share on other sites
eq    654
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
ajas95    767
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.

Share this post


Link to post
Share on other sites
eq    654
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.

Share this post


Link to post
Share on other sites
eq    654
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"

void
main(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.

Share this post


Link to post
Share on other sites
eq    654
Interseting thing:

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

Share this post


Link to post
Share on other sites
Quote:
Original post by ajas95
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.

This is not true. Object construction order is based on the order they appear in the translation unit relative to scope. Whether you use the object or not has no bearing. In the case of multiple translation units, as in this case, you have no guarantee that if one global/static member object uses another one in a separate translation that the second one is initialized first. It's russian roulette whether or not the result is what you'd want. When you first use an object has no bearing on when it is constructed. Again, as I said in my previous post, this is why people often use statics inside functions, since they are constructed the first time the function is called, making construction order (though not destruction order) clearly defined.

Edit: Actually, not in this case, since after reading his source code he's not relying on another object's construction.

[Edited by - Polymorphic OOP on November 8, 2004 6:52:10 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by eq
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.

This is strange. Using VC++ 7.1, I notice that if I switch the lib to just be an executable and run that, the constructor gets called as it should, otherwise, if I leave it as a lib an just link with it in the other executable, the constructor isn't called. I dunno what to tell you. Unless I missed something, I'm stumped.

Edit: I'm talking about TestA8::m_format's constructor in case I wasn't clear.

Share this post


Link to post
Share on other sites
eq    654
The "solutions" I've seen so far are basicly the modification I've done in my last post.
Basicly one .cpp file is created which references all objects in all libs that needs to be linked.
Here's another sample workspace using this method:

http://hem.bredband.net/b295424/test2.zip

This is semi-ok, but you must do more work than simple linking with the library to be able to use it correctly, namely adding the file "ImageFileFormat_lists.cpp" to you main executable project. I'd still like some "magic" code that forces something to be linked and executed.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
i can't help you with this specific problem on MSVC, but i have seen this on other compilers. in particular, Metrowerks C++ for PS2.

to get around this problem, we used the Metrowerks specific pragma "#pragma force_active on"

don't know what (if anything) MSVC might have to offer for that.

Share this post


Link to post
Share on other sites
eq    654
Thats exactly what I'm looking for, I do belive that GCC has a similar thing: __attribute__((used)) to force the linker to generate code for an object. I can't seem to find the eqvivalent for MSVC / Intel C++ though... any hints welcome.

Share this post


Link to post
Share on other sites
ajas95    767
Quote:
Original post by eq
Thats exactly what I'm looking for, I do belive that GCC has a similar thing: __attribute__((used)) to force the linker to generate code for an object. I can't seem to find the eqvivalent for MSVC / Intel C++ though... any hints welcome.


Here you can find out about the #pragma optimize directive.

It's basically like

#pragma optimize( "", off )
void foo()
{
int a = 5;
}
#pragma optimize( "", on )

Share this post


Link to post
Share on other sites
Leadorn    100
Quote:
Original post by Polymorphic OOP
Quote:
Original post by Leadorn
I use vc71 2003 and the constructor is called.

Odd... I tested it on VC++ 7.1 and it wasn't. Did you change any settings?


Ive retested it with a totaly new empty console project and it calls the constructor on the static Test test object.

Share this post


Link to post
Share on other sites
Quote:
Original post by Leadorn
Ive retested it with a totaly new empty console project and it calls the constructor on the static Test test object.

Yeah, with an empty project it works properly as I said earlier, but not with his example -- where the object is in a static link library.

Quote:
Original post by Namethatnobodyelsetook
If I recall, making it static makes it initialize on first use. Why not just make it global and non-static?

Not true, see previous replies.

Share this post


Link to post
Share on other sites
eq    654
Thanks for the effort guys, but this isn't an optimization issue I think.
Furthermore I'm more or less stuck with the Intel C++ compiler for other reasons and as shown above it doesn't seem to work with ANY other compiler/linker either. Funny though that GCC and metroworks compilers seems to have realize that there exists a problem and provided solutions for it. I've looked at possible pragmas and __declspec's but none seems to do what I want.

The most promising seemed to be the #pragma init_seg, but I'm a bit confused on how to use it and I'm quite sure it can't be used to do what I need: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/pragm_14.asp

Share this post


Link to post
Share on other sites
doynax    850
I can confirm that this problem exists with at least VC6.
We had to work around it, even for non trivial classes, by referencing dummy functions inside the static classes manually.

There's nothing strange about it though. The linker just reckons that since no one accesses the the class from the outside it can be safely removed since it doesn't seem to be used anyway.
It's not much of an issue since the one-in-a-million case where it happens to guess wrong can be easily worked around, the alternative would be to include a lot of unnecessary code just to be sure, which is much worse.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this