Wierd Behavior: Initialized const static member is empty.

Started by
8 comments, last by Ravyne 17 years ago
So, to the best of my knowlege I've done this correctly, however I encountered some strange behavior last night and I'm trying to find a solution. I have a 3x3 Matrix class, Mat3f, comprised of floats. It has all the typical Matrix/Matrix and Matrix/Vector operations, as well as some optimized routines for transforming an array of vertices or vectors all at once. It has a const static Mat3f member named IDENTITY which is initialized in the .cpp to the 3x3 identity matrix using one of Mat3f's constructors. While implimenting a Matrix Stack for 2D transforms last night, I noticed that the contents of my transformation matrix always contained the zero matrix. Eventually I traced this back to the fact that IDENTITY did not contain the identity matrix as I assumed it would, but contained the zero matrix instead. Because the transformation matrix was initially set to IDENTITY, subsequent multiplies by various transform matrices always resulted (correctly) in a zero matrix. Are there any rules or gotchas of which I am obviously unaware in regards to const static members? I wonder if its perhaps due to the fact that the member in question is of the same type as the class containing it? Maybe because the class must be fully defined (The class is pre-declared in the header as well) before one can initialize it? If so, is there any way around it? Here is a snippet of the header:

// ... more ...

class Mat3f;

// ... some friend function declarations ...

class Mat3f
{
public:
    Mat3f()					{ };

    Mat3f(const float, const float, const float,
          const float, const float, const float,
          const float, const float, const float);

    // ... method & operator declarations ...

    // Usefull constants
    static const Mat3f IDENTITY;
    static const Mat3f ZERO;

    float   m11, m21, m31,
            m12, m22, m32,
            m13, m23, m33;
};

// ... more ...



And here is the initialization statement from the .cpp file:


#include "mat3.hpp"

const Mat3f Mat3f::IDENTITY = Mat3f(1.0f, 0.0f, 0.0f,
                                    0.0f, 1.0f, 0.0f,
                                    0.0f, 0.0f, 1.0f);

Mat3f::Mat3f(const Mat3f &src)
{
	m11 = src.m11; m12 = src.m12; m13 = src.m13;
	m21 = src.m21; m22 = src.m22; m23 = src.m23;
	m31 = src.m31; m32 = src.m32; m33 = src.m33;
}

Mat3f::Mat3f(const float p11, const float p12, const float p13,
             const float p21, const float p22, const float p23,
             const float p31, const float p32, const float p33)
{
	m11 = p11; m12 = p12; m13 = p13;
	m21 = p21; m22 = p22; m23 = p23;
	m31 = p31; m32 = p32; m33 = p33;
}

I'm using Microsoft Visual Studio 2005 Professional (native C++) [edit]Whoops! Added constructors at Apoch's request. [Edited by - ravyne2001 on March 26, 2007 9:45:44 PM]

throw table_exception("(? ???)? ? ???");

Advertisement
Works fine for me here, with the same compiler. Can you post the code of the constructor in question?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

If you do a Lot of static initalisation, then some compilers do and do remove them depending on strange rules.

What I would suggest would be a static function within you Mat3f to return your Idenity Matrix.

Another way is to make all of your static's do their init in a start up fuction.

Those are the only 2 ways I know of forcing the init to happen.
Armand -------------------------It is a good day to code.
Done Apoch. Also, I'm building this as a library (debug build) and statically linking into the host application, which is also built in debug mode.

throw table_exception("(? ???)? ? ???");

Quote:Original post by Armand
If you do a Lot of static initalisation, then some compilers do and do remove them depending on strange rules.


I'm skeptical of this.

There is a well-known scenario where overuse of static code can cause problems, because nobody knows what order the static code is run in - this is generally referred to as the static order of initialization problem. However, no compiler should be "removing" static init code. That would be a very serious error.


ravyne2001: it's possible your linkage arrangement is part of the problem, but I can't see how. The code you've posted works fine for me when it's all in a single project, but I don't have time at the moment to set up a .lib and experiment with that. Best I can suggest is step through some code with the debugger and see if you can locate where the problem is coming from. You might find it handy to have the default constructor for Mat3f put random crap into the member variables (and possibly also put some crap into the ZERO matrix), so you can detect where the zero values are coming from.

A couple of general suggestions though: first, you don't need a copy constructor in this scenario (the compiler-generated one will do fine); and secondly, initializer lists are your friends.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Thanks for your help in investigating and for your debugging tips, I was planning that as my next course of action. As for the initializer list and copy constuctor, yes, I'm still in the process of cleaning up some of the old remnants like that :)

I'll post back with any findings from some more debugging, and again, thanks.

throw table_exception("(? ???)? ? ???");

Quote:Original post by ravyne2001
Done Apoch. Also, I'm building this as a library (debug build) and statically linking into the host application, which is also built in debug mode.


Log4cpp incorrectly initializes static members when linked as static lib (but not DLL) on VS2005 and some of more recent gcc versions. No fix has been offered by now.

The problem happens in main(), while trying to configure the root logger. Although one would expect all members to have been initialized by then, they are apparently not.

So yes, static initialization of members across libs can cause problems.
Thanks for that Antheus. Until a fix is introduced, I guess I will have to stick to DLLs, or re-design that element of the API.

To me that seems like some really strange behavior, particularly because I was using the IDENTITY matrix within another portion of the .LIB itself, not even the host application -- I would have expected at least the former case to work, since its not crossing the boundary between .lib and application. I guess that's the nature of these errors though.

On the other hand, I'm glad to hear that I've not made some trivial mistake or done something otherwise stupid :D


Thanks again for all your assistance.

throw table_exception("(? ???)? ? ???");

Quote:Original post by ravyne2001
To me that seems like some really strange behavior, particularly because I was using the IDENTITY matrix within another portion of the .LIB itself, not even the host application -- I would have expected at least the former case to work, since its not crossing the boundary between .lib and application. I guess that's the nature of these errors though.


No, it's not.

Make sure to check the link posted above about static initialization order fiasco.

You never have any kind of guarantee on when static members will be initialized.

Quote:Until a fix is introduced, I guess I will have to stick to DLLs, or re-design that element of the API.


The fix here applies to log4cpp, not the compiler. Compiler is fine.

Quote:Original post by Antheus
Quote:Original post by ravyne2001
Until a fix is introduced, I guess I will have to stick to DLLs, or re-design that element of the API.


The fix here applies to log4cpp, not the compiler. Compiler is fine.


My mistake, I misinterpretted what you had posted as due to the compiler.

Quote:
Quote:
To me that seems like some really strange behavior, particularly because I was using the IDENTITY matrix within another portion of the .LIB itself, not even the host application -- I would have expected at least the former case to work, since its not crossing the boundary between .lib and application. I guess that's the nature of these errors though.


No, it's not.

Make sure to check the link posted above about static initialization order fiasco.

You never have any kind of guarantee on when static members will be initialized.



I see the problem you're getting at here. This is really a rather nasty problem.

To sum up my current understanding: You can only rely on const static members of built-in types which are initialized with a literal statement. You can never rely on any type which is initialized by a constructor or init function.

throw table_exception("(? ???)? ? ???");

This topic is closed to new replies.

Advertisement