# The dangers of static variables

This topic is 3845 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

##### Share on other sites
Would the problem you're describing be as important if you had only used this assertation trick in .cpp files?

##### Share on other sites
Quote:
 Original post by TrillianWould the problem you're describing be as important if you had only used this assertation trick in .cpp files?

This is only a problem with static variables in header files. Our new implementation that doesn't cause 20 minute link times looks like this.

    // -- base assert definition    #define Assert_(mask, e, msg) do {                                                                  if(!(e)) {                                                                                          uint32 returnaddr;                                                                       GetRA_(returnaddr);                                                                                  NRadEngine::CJournal::Log msg;                                                                  if(CJournal::ShowAssert(mask, #e, __FILE__, __LINE__, __FUNCTION__,                                                     returnaddr))                                                         { Break_ }                                                                              } } while(0)

I posted this because I've seen a few publications and code bases that use static variables in a header file. It's fairly common.

EDIT: source is missing \ on each line because it messes up formatting in source tags

##### Share on other sites
Quote:
 Original post by David NeubeltThis is only a problem with static variables in header files.

These static variables aren't in header files. The fact that the definition of the macro that defines the static variables appears in a header file doesn't cause the static variables to exist in the header file.

If static variables are the cause of the problem, they would cause it just as well if the macro definition instead appeared directly in each source file. The problem isn't "static variables in header files" but "static variables".

A further nitpick is that "skipassert##__LINE__" won't do what you imply it does, and whilst there are workarounds to make it do what is intended, there is no way that the value of __FILE__ could ever be part of a valid variable name, since __FILE__ is replaced with a string.

##### Share on other sites
Quote:
 Original post by Nathan BaumThese static variables aren't in header files. The fact that the definition of the macro that defines the static variables appears in a header file doesn't cause the static variables to exist in the header file.If static variables are the cause of the problem, they would cause it just as well if the macro definition instead appeared directly in each source file. The problem isn't "static variables in header files" but "static variables".

According to a developer, on Microsoft's linker, I was corresponding with during this ordeal, he told me that the cost would not apply if the assert were not in a header file.

Quote:
 A further nitpick is that "skipassert##__LINE__" won't do what you imply it does, and whilst there are workarounds to make it do what is intended, there is no way that the value of __FILE__ could ever be part of a valid variable name, since __FILE__ is replaced with a string.

We have multiple shipping products on multiple platforms and compilers with that code. I'm pretty sure it works. (EDIT: it doesn't quite work and the code doesn't look like that)

[Edited by - David Neubelt on July 14, 2007 10:10:46 PM]

##### Share on other sites
Quote:
 Original post by David NeubeltAccording to a developer, on Microsoft's linker, I was corresponding with during this ordeal, he told me that the cost would not apply if the assert were not in a header file.

I can get further clarification on why it only applies for header files if you are curious why.

-= Dave

##### Share on other sites
The only way I can see this having any effect on link times is if you were doing the assert in inline functions that were getting used very frequently, in which case the linker would have to resolve those symbols to the same memory location. But as Nathan pointed out, what you have shown wouldn't even compile since __FILE__ is replaced with a string. A lie, it does compile because your variable is literally called "skipassert__LINE____FILE__" because the macros aren't expanded before concatenating the symbol. Ever used this macro twice within the same scope?

Quote:
 Original post by David NeubeltAccording to a developer, on Microsoft's linker, I was corresponding with during this ordeal, he told me that the cost would not apply if the assert were not in a header file.

The linker doesn't even know header files exist, that's what the preprocessor is for.

##### Share on other sites
Quote:
 Original post by joanusdmentiaThe only way I can see this having any effect on link times is if you were doing the assert in inline functions that were getting used very frequently, in which case the linker would have to resolve those symbols to the same memory location.
Yes you are correct - I didn't relay all of the information. The cost only applies when the assert is in a header file and the function containing the assert has multiple instances of the assert.

Quote:
 But as Nathan pointed out, what you have shown wouldn't even compile since __FILE__ is replaced with a string.

Like I said, multiple shipping products, I'm not making this up but since you refuse to believe me then please compile this.
#define MyAssert static bool eAssertSkipped_##__FILE__##__LINE__ = false;int main() {    MyAssert;    return 0;}

-= Dave

[/source]

##### Share on other sites
I corrected my post as you were writing your reply. It compiles, but try using it twice and it won't.

#define MyAssert static bool eAssertSkipped_##__FILE__##__LINE__ = false;int main() {    MyAssert;    MyAssert; // ERROR: eAssertSkipped___FILE____LINE__ already defined    return 0;}

##### Share on other sites
Quote:
 Original post by joanusdmentiaI corrected my post as you were writing your reply. It compiles, but try using it twice and it won't.

Hmm - you're right - I wonder how we got it to work, I need to dig up source history when I get to work and correct the original post.

##### Share on other sites
If you did the following I think it would do what was expected, but then wouldn't compile because __FILE__ is replaced with a string as mentioned previously:

#define CONCAT(a,b) a##b#define MyAssert static bool CONCAT(eAssertSkipped_, CONCAT(__FILE__, __LINE__)) = false;

##### Share on other sites
Quote:
Original post by David Neubelt
Quote:
 Original post by Nathan BaumThese static variables aren't in header files. The fact that the definition of the macro that defines the static variables appears in a header file doesn't cause the static variables to exist in the header file.If static variables are the cause of the problem, they would cause it just as well if the macro definition instead appeared directly in each source file. The problem isn't "static variables in header files" but "static variables".

According to a developer, on Microsoft's linker, I was corresponding with during this ordeal, he told me that the cost would not apply if the assert were not in a header file.

That sounds like it should be a Microsoft problem, but you said it also applied to GCC and SN (I assume you mean the tools developed by SN Systems?).

Per the standard, there is no difference between code included from a header file and the same code directly placed in the source file. Any deviation from that is therefore deviation from the standard.
Quote:
 Original post by David NeubeltI can get further clarification on why it only applies for header files if you are curious why.

I would be interested to know what he says, yes.

Offhand, the only way any half-way sane linker would care about whether a macro which defined a variable was itself defined in a header is if there was some strange interaction with precompiled headers. That requires that GCC's and Microsoft's unrelated precompiled headers implementation have the same bug, which seems unlikely to me.
Quote:

Quote:
 A further nitpick is that "skipassert##__LINE__" won't do what you imply it does, and whilst there are workarounds to make it do what is intended, there is no way that the value of __FILE__ could ever be part of a valid variable name, since __FILE__ is replaced with a string.

We have multiple shipping products on multiple platforms and compilers with that code. I'm pretty sure it works.

On the other hand, I've just tested the exact code locally and it doesn't "do what you imply" (although it "works", for suitable values of "works").
#define Assert_(e) static bool skipassert##__LINE__##__FILE__ = false;Assert_(x == 0);

piped through the GNU C preprocessor, produces
# 1 "<stdin>"# 1 "<built-in>"# 1 "<command line>"# 1 "<stdin>"static bool skipassert__LINE____FILE__ = false;;

It's possible that Microsoft's preprocessor produces different output (although it shouldn't: GNU CPP's output is standard), but you implied that you'd tested the code on other compilers, including GCC. (That is what I was lead to understand from you saying that the problem manifested on other compiler systems.)

##### Share on other sites
Quote:
Original post by David Neubelt
Quote:
 Original post by joanusdmentiaThe only way I can see this having any effect on link times is if you were doing the assert in inline functions that were getting used very frequently, in which case the linker would have to resolve those symbols to the same memory location.
Yes you are correct - I didn't relay all of the information. The cost only applies when the assert is in a header file and the function containing the assert has multiple instances of the assert.

Ah-ha. That could at least make some kind of sense. Notwithstanding possible precompiled header weirdness, the problem is still not actually that the definition appears in a header, but rather that you have identically named static variables in identically named inline functions in lots of files. Using a header makes it easier to do that, but isn't the actual cause.
Quote:
Original post by David Neubelt
Quote:
 Original post by joanusdmentiaI corrected my post as you were writing your reply. It compiles, but try using it twice and it won't.

Hmm - you're right - I wonder how we got it to work, I need to dig up source history when I get to work and correct the original post.

__LINE__ can be fixed:

#define CONCAT3_(a,b,c) a ## b ## c#define CONCAT3(a,b,c) CONCAT3_(a,b,c)#define Assert_(e) \static bool CONCAT3(skipassert_, __LINE__, SOURCE_NAME) = false; \ if (CONCAT3(skipassert_, __LINE__, SOURCE_NAME) == false) \ // -- do assertion logic#define SOURCE_NAME FileNameCPPAssert_(x)

Ways to remove the the quotes and the period from __FILE__ are welcome.

(Note: You can use backslashes if you follow them with horizontal whitespace.)

##### Share on other sites
Quote:
 Original post by Nathan BaumWays to remove the the quotes and the period from __FILE__ are welcome.

What does the __FILE__ being part of the variable name buy you? Is there some funny business with inline functions and static variables? Because, other than that, wouldn't static make the variable unique to a given function and thus to a given file?

Actually... how do inline and static variables work together?

##### Share on other sites
Each instance of an inlined function reference the same static local variable (ie. each inlining doesn't get it's own version of the variable). You're right that the __FILE__ doesn't actually contribute anything to the uniqueness of the static variable.

##### Share on other sites
Quote:
Original post by Way Walker
Quote:
 Original post by Nathan BaumWays to remove the the quotes and the period from __FILE__ are welcome.

What does the __FILE__ being part of the variable name buy you?

I have no idea. I was just trying to make a working version of David's code.
Quote:
 Actually... how do inline and static variables work together?

More than one definition of the same inline function can appear in a program. That means inline functions can be defined in headers. (3.2.5)

A static local variable in an inline function always refers to the same object. (7.1.2.4)

##### Share on other sites
Looking over our code, I was mistaken and the static variable in our old code was simply a static flag within a do while scope. This allowed multiple copies of the flag in the same function.

It was having many copies of that variable in an inline function that caused the long link time.

-= Dave