3

Inclusion guards (as well as the #pragma once) in C header files are frequently used and well described in a wikipedia article and a way to prevent mistakingly double include a header file.

This questions asks in which cases it would be desired, in C programming, to make a double inclusion after all?

update This is not to suggest that I assume a priori that there was a good reason (some sort of preprocessor magic) worthwhile the negative effect of know requiring each programmer the overhead of generating include guards. It seems odd that the preprocessor behaves so inconveniently and I though there must be a valid reason? The question ask for such reasons, i.e. usefulness of a double inclusion

update 2 As a paraphrase to the question I would assume the following question:

Is the double inclusion problem (solved by include guards & #pragma once) simply a preprocessor imperfection, which its causes being sorts of historical?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
humanityANDpeace
  • 4,350
  • 3
  • 37
  • 63
  • 5
    Nowhere, never. Why'd you ask? – Sourav Ghosh Jan 16 '17 at 17:14
  • The preporcessor expands headers, multiple includes is literally duplicating code, causing all kinds of re declaration/ definition mischief. There is no good reason, ever. – George Jan 16 '17 at 17:16
  • 2
    It happens all the time by indirect inclusion. b.h includes a.h. c.h also includes a.h. You write a module that needs to include b.h and c.h. If you didn't have the guards, you'd get two inclusions of a.h. You could choose not to allow headers to include other headers and keep track of all the dependencies yourself. I worked on a system like that. It was terrible. – Gene Jan 16 '17 at 17:16
  • And even if you manage to conjure some preprocessor magic, you're not winning any design awards. – DeiDei Jan 16 '17 at 17:17
  • 2
    Because if there was no actual use case, it seems stupid, that it is possible to include the same file twice, in the first place., so the question asks why? what for? reasons? rationale? – humanityANDpeace Jan 16 '17 at 17:17
  • 5
    @SouravGhosh: Actually everywhere. Multiple inclusing is the normal case. Thus the guards. – too honest for this site Jan 16 '17 at 17:17
  • If I recall correctly, C was not designed originally to use headers the way we use them now. It kind of developed organically after C was created and now remains for backwards compatibility reasons. – Alden Jan 16 '17 at 17:21
  • @Olaf Right sir, but I'd say that's not _desired_, :) – Sourav Ghosh Jan 16 '17 at 17:22
  • 1
    The pre-processor has no knowledge of C syntax and is a very simple tool. It has probably been around too long now for it to be altered to behave differently - it is feasible that in some dark corner somewhere is lurking code which relies on this behaviour. To be honest if this was the worst thing about the pre-processor then we would be laughing.... – cdarke Jan 16 '17 at 17:26
  • @Olaf Just to clarify, header guards == less manual checking but that does not mean _intentional_ multiple includes, right? :) – Sourav Ghosh Jan 16 '17 at 17:29
  • 1
    If you are brave, look at [Boost.Preprocessor](http://www.boost.org/doc/libs/1_63_0/libs/preprocessor/doc/index.html), you'll find some use-cases. – Mat Jan 16 '17 at 17:32
  • @SouravGhosh: It is desired. Simply because you should include a header whereever you need it. Which implies you shoud include it for example in the implementation and header of a library. Which is the most simple case of indirect inclusing, but also **intentional** double inclusion. And that's what guards (or the pragma) are _intended_ for. – too honest for this site Jan 16 '17 at 17:33
  • Take a look at my answer [here](http://stackoverflow.com/questions/38504840/is-there-a-reason-why-someone-would-include-stdlib-h-twice/38505183#38505183) – Eugene Sh. Jan 16 '17 at 17:35
  • The question is too opinionated. Whether it is an "imperfection" or intended behaviour can hardly be said without rational from the very first author. – too honest for this site Jan 16 '17 at 17:37
  • @olaf There are two answers already. And while opinion cannot completely be excluded, I phrased the question to ask for actual use cases, which is much less of an opinion and more of a "provide a list of options....." and can be answered – humanityANDpeace Jan 16 '17 at 17:41
  • @humanityANDpeace: The number of answers does not mean it is not opiniated. Actually the more answers the more likely it is. And that does not only apply to the rational, but also the use-cases. But feel free to see it as to broad alternatively. – too honest for this site Jan 16 '17 at 17:46
  • @olaf Reading your discussion/comment with SouravGhosh, I would like to confirm that you remarkes might mean that iyho, double inlusion is a use case, because of it being good practise to reference all the desired dependen headers that are used? It seems strange to me, since that is not a use case to me, as include guards, to not really make that a double/multiple inclusion, right? – humanityANDpeace Jan 16 '17 at 17:46
  • So if you have a guard you consider it not double-inclusing, but if you have some other conditional compilation it is? How many lines of code do you consider double inclusing starts? Do comments count? Conditional proprocessor statements? other includes? ... - got the idea? – too honest for this site Jan 16 '17 at 17:52
  • @Olaf exactly, that is how I would interpret the existance of guards, they prevent double/multiple inclusion, hence it does not happen. – humanityANDpeace Jan 16 '17 at 17:56
  • @humanityANDpeace: Last try: Does cpp know about the guard **before** it has included the file? And does it have to read the **whole file** to see where it ends? If the answert is yes, it **is** included already, whether the guard evaluates true of false. So, what do they prevent? Only execution of the C declarations in the header. – too honest for this site Jan 16 '17 at 17:58
  • @Olaf, I agree that it is somewhat vague where we should draw the line "inclusion happend", and you are right to say that the preprocessor technically included the file twice/multiple times. I think with your attention to detail demonstrated you are yet able to understand that the question is yet about **the merits of those cases where the included files would not have a include guard protection** (as is for example given in both existing answers). Again I agree that you are right technically, especially when you say inclusion happens before evaluation of the include guards... – humanityANDpeace Jan 16 '17 at 18:06
  • It is not vague. It is very clear. If the file contents is evaluated, it is included. If not, it is not. So if you'd put the guard around the `#include` (which would also be possible), it was not, otherwise it is. It is that simple. Don't confuse the cpp with the cc. – too honest for this site Jan 16 '17 at 18:33

4 Answers4

2

There is no imperfection. Including a file as many times as a #include statement is found is what it should do.

Check chapter 7.2 of the standard:

The assert macro is redefined according to the current state of NDEBUG each time that assert.h is included.

This implies that you could use it like this:

#define NDEBUG
#include <assert.h>
assert( <expr1> );

#undef NDEBUG
#include <assert.h>
assert( <expr2> );

Whether this is very useful or not may be a different story.

You could also use your header to define something similar to X-Macros in a header.

header.h

  ELEMENT(x, _a_, _b_)
  ELEMENT(y, _a_, _b_)
  ELEMENT(z, _a_, _b_)

source.c

#define ELEMENT(_x_, _a_, _b_) printf("%s: %d, %d", _x_, _a_, _b_);
  #include <header.h>

#define ELEMENT(_x_, _a_, _b_) {_x_, _a_, _b_},
struct something xy[] = {
  #include <header.h>
};

There might be cases where it cannot be solved by X-Macros

Gerhardh
  • 11,688
  • 4
  • 17
  • 39
  • Wow! It is really nice to see some application for a double/multiple inclusion of header files. You yourself do not seem very convinced about the usefulness of such use cases, e.g. preprocessor magic etc. Would you say that having a special `#pragma multiple_times_inclusion_desired` preprocessor macro would be more close to average usage, i.e. that in most cases (the norm/standard) one rather would not want such double inclusion? – humanityANDpeace Jan 16 '17 at 17:38
  • True, I do not really like this usage. ;) But it seems to be intentional. I have seen such multiple inclusions in some auto generated code but it was rather ugly and I do not recall all the details. Maybe they used it because macros could not have variable arguments at that time or the compiler war old.... I agree that it is not the normal usage of header and your suggested #pragma looks OK. – Gerhardh Jan 16 '17 at 17:45
  • @Downvoter: Could you please drop some comment about your reasons? – Gerhardh Jan 16 '17 at 17:46
1

The #include directive is not limited to headers. There is an uncommon practice of including prepared blocks of executable code in the middle of source code.

For example:

max = calculateMax(a, b, c);
#include "debug-helper.c"; // NOTE: It may be cleaner to organize helper into functions
...

This would not work correctly (i.e. in all cases) with implicit and obligatory header guards.

Grzegorz Szpetkowski
  • 36,988
  • 6
  • 90
  • 137
  • 1
    Looks like someone in a bad mood today. BTW, thre is an actual user with this name, so you've just summoned him :) – Eugene Sh. Jan 16 '17 at 17:37
  • I am sorry for your answer having been downvoted, and also wonder why, as indeed it shows some case (maybe not that common though) which gives a reason for allowing multiple inclusion. – humanityANDpeace Jan 16 '17 at 17:49
0

Is the double inclusion problem (solved by include guards & #pragma once) simply a preprocessor imperfection, which its causes being sorts of historical?

There is no double inclusion problem; rather, there is a multiple definition problem. Though it depends on the content of the header, without multiple-inclusion guards it would be relatively common for multiple inclusion of the same header to effect disallowed multiple definition of one or more identifiers. The convention of using multiple-inclusion guards was developed to protect against that, and it has become routine practice even for headers that don't actually present a multiple-inclusion problem.

Functions and objects should not be defined in headers, though declaring these in headers is one of headers' main use cases. It's fairly easy to avoid that aspect of the issue. It used to be the case, however, that it was an technically an error to redeclare the same typedef name, even with an identical definition, and some pre-standardization compilers rejected even identical redefinition of macros. Neither of these is an issue in modern C (C2011).

What does remain an issue is redefinition of struct, union, and enum types -- this is still disallowed, even if the redefinition changes nothing. Secondarily, it is not uncommon for the meaning of the contents of a header file to be dependent on symbols that may be defined elsewhere in the translation unit. If such a header's contents are included more than once in the TU then it is possible for them to have different meaning on the two inclusions. That can lead to incompatible multiple declarations.

The point to be understood is that whether multiple-inclusion of the contents of a given header is wrong is a function of those contents.

Additionally, I already described how conditional compilation makes it possible for the meaning of a header to differ depending on where it is included relative to the rest of the translation unit. Although this can contribute to producing disallowed incompatible declarations or multiple definitions, it can also produce perfectly valid code.

TL;DR: multiple inclusion of header files is not inherently wrong, and can serve a useful purpose. It is therefore not a flaw that the C preprocessor does not automatically protect against multiple inclusion. It is reasonable for the responsibility to rest on the contents of each header to exhibit suitable behavior when that header is included more than once into the same translation unit.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
0

One place where I've used multiple includes of the same file is as a glorified super-sized macro.

#define ARG1 "First arg"
#define ARG2 345
#include "run.macro"

The "run.macro" file would then undefine ARG1 and ARG2 when its finished, so it can be used all over the place.

I've used this trick to refactor a large system of macros into something much more manageable (and far better commented), though in the longer term I continued to refactor it until it was removed entirely.

In short, it can be useful for refactoring, but I wouldn't recommend it as a general practice.

Charles Ofria
  • 1,936
  • 12
  • 24