Comment Before Header Guards

Started by
20 comments, last by GameDev.net 17 years, 10 months ago
Quote:Original post by SiCrane
Quote:Original post by jpetrie

  • exit(): When you call exit(), the calling process is terminated after cleanup and functions registered with atexit() are called. Static objects are destroyed in the opposite order they were created. This is basically equivalent to returning from main().



Objects with automatic storage duration (on the stack) do not get destroyed on calling exit(), so may leak resources.


Here's a demonstration of that point (notice how foo's destructor is never called):
#include <iostream>class foo{    public:    ~foo()  {std::cout << "foo's destructor called\n"; }};class bar{    public:    ~bar()  {std::cout << "bar's destructor called\n"; }};int main(){    foo a;    static bar b;        exit(0);}


Outputs:
bar's destructor called
Advertisement
Quote:Original post by jpetrie
Quote:
Depending on how sophisticated it is, it may work. It would be easy to detect sections of the form #ifndef FOO_H/#define FOO_H/.../#endif and remember whether FOO_H is still defined (or, in your case, both FOO and BAR) when another #include attempt is found. Hey, look at that, it even works when it's not an include guard!


It seems you're missing the point now. FOO and BAR in that example might not be inclusion guards at all. If your optimization guessed they were and failed to include the file multiple times from the same translation unit, you may have just broken my build (and since not having include guards and multiply-including files is not in violation of any kind of specification w.r.t. the preprocessor, your implementation is thus buggy and broken).


Here's the thing, it doesn't matter whether it's an include guard or not so long as the optimization only catches cases that behave as if they were include guards. From the code you showed, your FOO and BAR look like include guards. I don't know if gcc would catch this because have two guards in one file is much less common than having a single guard.

Quote:
Quote:
And, for what it's worth, gcc implements this optimization. Compiled with the -H option and the include guards it prints ". header.h" only once. If the include guards are removed it prints ". header.h" twice (and gives an error about redefining 'class foo').

So it does. It's also -- as I suspected it would have to be -- very, very conservative about applying it. Any non-comment characters outside of the first #ifndef block cause it to abort the optimization (interestingly enough, those characters don't even have to be legal code). You can also manually break it by doing #undef HEADER_H_ or whatever after the include, which does suggest that it associates the filename with the symbol used to guard it, as your theorized, and checks to make sure its still defined.


Well, calling this "very, very conservative" and saying a #undef "breaks" it seems a bit extreme to me. As it is, the vast majority of include guards will be robustly and correctly optimized without violation of the standard. For further optimization, you'd have to beef up the preprocessor to actually understand C/C++ (instead of just being able to tokenize it).

Quote:
Quote:
But it isn't that hard to detect robustly or correctly since the idiom is so common and varies so little.

"Robustly and correctly" here refers to being able to correct determine a set of preprocessor commands is an include guard in the general case so that you can apply your proposed optimization, or not, as required to maintain the build. You cannot do this, you can only guess, and must be very conservative about your guess. You can probably apply the optimization only when the top nesting level of #if* / #endif pairs contains only one pair.

(EDIT: And no other code or preprocess options at that level. Tests suggest that this is exactly what GCC does, see below).


This seems no great limitation to me. I don't know that I've ever seen code outside include guards except in cases where it was intended to be included multiple times. Thus, in my experience, any time #pragma once could've been applied, include guard optimization could also be applied.

Quote:
Quote:
Then I assume you don't see any reason to use #pragma once?

On the contrary. #pragma once is useful for doing what #pragma once does (typically): tell the preprocess never to include this file again. #pragma once is an explicit declaration to the tools that you never want this file included again this translation unit. The preprocessor commands #ifndef, #define, and #endif, on the other hand, are mechanisms for controlling the preprocessing of source code that happen to have a common idiomatic usage pattern that helps prevent multiple-definition errors when a file gets included multiple times from the same translation unit. They are not the same.


I don't claim that they're the same, I do claim that the #ifndef/#define/#endif is so idiomatic that any programmer with a reasonable amount of experience in C/C++ will recognize it as an include guard (just like they wouldn't have to parse "for (int i = 0; i <

This topic is closed to new replies.

Advertisement