82

What's the point of telling the compiler specifically to include the file only once? Wouldn't it make sense by default? Is there even any reason to include a single file multiple times? Why not just assume it? Is it to do with specific hardware?

PSkocik
  • 52,186
  • 6
  • 79
  • 122
Johnny Cache
  • 969
  • 5
  • 9
  • 24
    *Is there even any reason to include a single file multiple times?* => Could be. A file might have conditional compilation `#ifdefs` in it. So you might say `#define MODE_ONE 1` and then `#include "has-modes.h"`, and then `#undef MODE_ONE` with `#define MODE_TWO 1` and `#include "has-modes.h"` again. The preprocessor is agnostic about these kinds of things, and maybe sometimes they can make sense. – HostileFork says dont trust SE Nov 21 '18 at 16:40
  • 66
    It would make sense as the default. Just not the one they picked when C programmers still rode horses, carried guns and had 16KB of memory – Hans Passant Nov 21 '18 at 16:41
  • 1
    @FrançoisAndrieux but the duplicate seems to ask same thing about include guards in general not only #pragma once. – Öö Tiib Nov 21 '18 at 16:43
  • 11
    You can include `` multiple times, with different definitions of `NDEBUG`, in the same source file. – Pete Becker Nov 21 '18 at 16:45
  • 1
    @FrançoisAndrieux Fair enough. That should be more clear. – Johnny Cache Nov 21 '18 at 16:47
  • 3
    As for `#pragma once` itself, there are hardware environments (typically with networked drives and possible multiple paths to the same header) where it won't work right. – Pete Becker Nov 21 '18 at 16:48
  • It would be a breaking change, and would require a new means to disable it - two good reasons not to change. – Toby Speight Nov 21 '18 at 16:50
  • 1
    Y'all answering in comments: the dupe target is still open for answers... – Baum mit Augen Nov 21 '18 at 16:51
  • @BaummitAugen: But that question is not asking about `#pragma once`, so those answers would not be appropriate. It asks about include-guards. – Nicol Bolas Nov 21 '18 at 16:52
  • @NicolBolas All answers in this comment thread appear to apply the exact same way to classical include guards as they do to `#pragma once` afaict. – Baum mit Augen Nov 21 '18 at 16:54
  • @BaummitAugen: Irrelevant. The "duplicate" is not asking about `#pramga once`, so an answer explaining *only* how `#pragma once` doesn't work would be off-topic. Notice how the answer to this question doesn't talk about include-guards or even the failures of `#pragma once`; it instead talks about where multiple inclusions are useful. Which would also not be an appropriate answer to the "duplicate". – Nicol Bolas Nov 21 '18 at 16:55
  • 2
    @JohnnyCache should I interpret your last comment in a way that you are not really interested in an answer, but rather are trolling and mark your question for moderators as such? – SergeyA Nov 21 '18 at 17:07
  • @SergeyA I'm very much interested in an answer, but the fact that people keep marking and unmarking it as duplicate is curious as well. In fact, that's why I've commented with that remark, instead of editing it into the question. – Johnny Cache Nov 21 '18 at 17:09
  • I remember multiple inclusion of the same headers being used in `c` more than we would use it in `c++`. A compiler would not want to break this usage however. – drescherjm Nov 21 '18 at 17:16
  • 12
    If you have `#pragma once` assumed, what is the way of countermanding that default? `#pragma many`? How many compilers have implemented anything like that? – Jonathan Leffler Nov 21 '18 at 17:20
  • 1
    @JonathanLeffler That's not a real argument though. If default `#pragma once` and an opt-out `#pragma many` was implementable and considered a good idea to the point that the standard wants it, compilers would implement it. The question here is essentially why that is not happening. – Max Langhof Nov 21 '18 at 17:46
  • 1
    @MaxLanghof: It's mainly pointing out that changing the default requires a way to ensure that `` — to name (once again) the Standard C example of a header that must be includable multiple times in a single TU with (potentially) _different_ effects on each occasion — continues to work correctly. Making `#pragma once` the default probably won't happen because it would disrupt working code, and there are already other workable alternatives available within the standard. One day, maybe, there might be `#pragma STDC once` — which would nominally be the way it would be standardized. – Jonathan Leffler Nov 21 '18 at 18:19
  • 2
    TL;DR because the C++ build system is an ancient joke and programmers have come to depend on its quirks – alter igel Nov 21 '18 at 18:28
  • I do not want to know how much back-room fighting there was over re-purposing `auto`. – user4581301 Nov 21 '18 at 19:13
  • 2
    Also see [#pragma once vs include guards?](https://stackoverflow.com/questions/1143936/pragma-once-vs-include-guards/34884735#34884735) and the part ***#pragma once has unfixable bugs. It should never be used.*** As I understand it, headers created through links create a lot of problems. – jww Nov 21 '18 at 19:40
  • 1
    Possible duplicate of [#pragma once vs include guards?](https://stackoverflow.com/a/34884735/608639) – jww Nov 21 '18 at 19:44
  • 2
    @Hans Passant: Sorry, but I still ride my horse, and have been known to carry a gun when I do, since there are bears & mountain lions out there. – jamesqf Nov 22 '18 at 05:29

6 Answers6

86

There are multiple related questions here:

  • Why is #pragma once not automatically enforced?
    Because there are situations in which you want to include files more than once.

  • Why would you want to include a file multiple times?
    Several reasons have been given in other answers (Boost.Preprocessor, X-Macros, including data files). I would like to add a particular example of "avoid code duplication": OpenFOAM encourages a style where #includeing bits and pieces within functions is a common concept. See for example this discussion.

  • Ok, but why is it not the default with an opt-out?
    Because it is not actually specified by the standard. #pragmas are by definition implementation-specific extensions.

  • Why has #pragma once not become a standardized feature yet (as it is widely supported)?
    Because pinning down what is "the same file" in a platform-agnostic way is actually surprisingly hard. See this answer for more information.

Max Langhof
  • 22,398
  • 5
  • 38
  • 68
  • [And another take on why not standardize `once`](https://stackoverflow.com/a/34884735/4581301) – user4581301 Nov 21 '18 at 19:05
  • 4
    In particular, [see this example](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52566) for a case of where `pragma once` fails but include guards would have worked. Identifying files by location doesn't work either because sometimes the same file occurs multiple times in a project (e.g. you have 2 submodules that both include a header-only library in their headers and check out their own copy of it) – M.M Nov 21 '18 at 19:46
  • 6
    Not all pragmas are implementation-specific extensions. E.g. [`#pragma STDC` family](https://en.cppreference.com/w/c/preprocessor/impl#Standard_pragmas). But they all do control implementation-defined behavior. – Ruslan Nov 21 '18 at 20:58
  • 4
    @user4581301 This answer over exaggerates the problem with pragma once and does not consider troubles due to include guards. In both case some discipline is needed. With include guards one must make sure to use a name that will not be used in an other file (which will happen after a file copy modify). With pragma once, one has to decide what is the unique right place for its file, which is a good thing after all. – Oliv Nov 21 '18 at 22:42
  • So I'd say two files are the same if `fwrite`'ing to one modifies the other... I think that's reasonable? So I guess the question is not actually detecting if two files are the same, but rather doing so without actually writing to them? – user541686 Nov 22 '18 at 00:06
  • @Mehrdad: It would be reasonable for the Standard to say that an implementation is free to ignore `#pragma once` in any case where, for whatever reason, it believes (rightly or wrongly) that two files may be distinct, but requiring that an implementation read everything in a file that is going to be skipped falls in the realm of Just Plain Dumb. Perhaps a "assume remainder of file is blank except for any required balancing `#endif` directives would be more useful, but there's no reason not to encourage implementations to provide an efficient means of... – supercat Nov 22 '18 at 01:51
  • ...avoiding excessive file accesses during compilation on a best-effort basis. – supercat Nov 22 '18 at 01:51
  • @supercat: Confused, why would you need to read the whole file that might be skipped? The implementation could just write 1 character to the `#pragma once` itself and see if it changes the other file it's trying to test against. – user541686 Nov 22 '18 at 02:16
  • 3
    @Mehrdad: Are you seriously suggesting that compilers write to source files!? If a compiler sees `#ifndef XX`, it must can't know whether there's anything following the corresponding `#endif` until it's read the entire file. A compiler that keeps track of whether the outermost `#ifndef` encloses the entire file and notes what macro it checks may be able to avoid rescanning the file, but a directive to *say* there's nothing of importance following the current point would seem seem nicer than relying upon compilers to remember such things. – supercat Nov 22 '18 at 06:47
  • @supercat: no, you're just not reading. I said *"So I guess the question is not actually detecting if two files are the same, but rather doing so without actually writing to them?"* to which you objected and said it's dumb to read a file that you would skip to which I responded that is not necessary to which you responded by forgetting what I wrote... the rest of what you wrote doesn't really make sense either, `#pragma once` already does something and that is one way to do it. – user541686 Nov 22 '18 at 07:06
  • @supercat implementation is already free to ignore pragma once or any pragmas really. Hence why there is no pressure to standardize it, since you can already use it and get "mostly working but slightly different on all platforms" behavior. Actual introduction of it into standard would bring nothing more to the table if it's not actually specified how ti works and this is hard as discussed. – Dan M. Nov 22 '18 at 15:04
  • Good standards should offer recommendations instead of just requirements; in many cases, the former are *far* more valuable. – supercat Nov 22 '18 at 15:58
  • @GiacomoAlzetta There are other concerns (network drives for example). And as a compiler writer, you can't just tell people (or rather corporate "customers") "f*ck your code". – Max Langhof Nov 23 '18 at 08:44
38

You can use #include anywhere in a file, not just at global scope - like, inside a function (and multiple times if needed). Sure, ugly and not good style, but possible and occasionally sensible (depending on the file you include). If #include was only ever a one time thing then that wouldn't work. #include just does dumb text substitution (cut'n'paste) after all, and not everything you include has to be a header file. You might - for example - #include a file containing auto generated data containing the raw data to initialize a std::vector. Like

std::vector<int> data = {
#include "my_generated_data.txt"
}

And have "my_generated_data.txt" be something generated by the build system during compilation.

Or maybe I'm lazy/silly/stupid and put this in a file (very contrived example):

const noexcept;

and then I do

class foo {
    void f1()
    #include "stupid.file"
    int f2(int)
    #include "stupid.file"
};

Another, slightly less contrived, example would be a source file where many functions need to use a large amount of types in a namespace, but you don't want to just say using namespace foo; globally since that would polute the global namespace with a lot of other stuff you don't want. So you create a file "foo" containing

using std::vector;
using std::array;
using std::rotate;
... You get the idea ...

And then you do this in your source file

void f1() {
#include "foo" // needs "stuff"
}

void f2() {
    // Doesn't need "stuff"
}

void f3() {
#include "foo" // also needs "stuff"
}

Note: I'm not advocating doing things like this. But it is possible and done in some codebases and I don't see why it should not be allowed. It does have its uses.

It could also be that the file you include behaves differently depending on the value of certain macros (#defines). So you may want to include the file in multiple locations, after first having changed some value, so you get different behaviour in different parts of your source file.

Jesper Juhl
  • 1
  • 3
  • 38
  • 63
  • 1
    This would still work if all headers were pragma once. As long as you didn't include the generated data more than once. – PSkocik Nov 21 '18 at 16:57
  • 2
    @PSkocik But maybe I *need* to include it more than once. Why shouldn't I be able to? – Jesper Juhl Nov 21 '18 at 16:59
  • 2
    @JesperJuhl That's the point. You won't _need_ to include it more than once ever. You currently have the option, but the alternative isn't much worse, if at all. – Johnny Cache Nov 21 '18 at 17:08
  • 9
    @PSkocik If I change the value of `#define`s before each include that changes the behaviour of the included file then I may very well need to include it multiple times to get those different behaviours in different parts of my source file. – Jesper Juhl Nov 21 '18 at 17:28
27

Including multiple times is usable e.g., with the X-macro technique:

data.inc:

X(ONE)
X(TWO)
X(THREE)

use_data_inc_twice.c

enum data_e { 
#define X(V) V,
   #include "data.inc"
#undef X
};
char const* data_e__strings[]={
#define X(V) [V]=#V,
   #include "data.inc"
#undef X
};

I don't know about any other use.

PSkocik
  • 52,186
  • 6
  • 79
  • 122
  • That sounds overly complex. Any reason not to just include those definitions in the file in the first place? – Johnny Cache Nov 21 '18 at 16:50
  • 2
    @JohnnyCache: The example is a simplified version of how X-macros work. Please read the link; they're extremely useful in some cases for doing complex manipulations of tabular data. In any significant usage of X-macros, there would be no way you could just "include those definitions in the file". – Nicol Bolas Nov 21 '18 at 16:51
  • 2
    @Johnny - yes - one good reason is to ensure consistency (hard to do by hand when you have just a few dozen elements, never mind hundreds). – Toby Speight Nov 21 '18 at 16:51
  • @TobySpeight Heh, I suppose I could spare a single line of code to avoid writing thousands somewhere else. Makes sense. – Johnny Cache Nov 21 '18 at 16:52
  • 1
    To avoid duplication. Especially if the file is large. Admittedly, you could just use a big macro containing the X macro list but since projects could be using this, mandating `#pragma once` behavior would be a breaking change. – PSkocik Nov 21 '18 at 16:52
  • In many cases, the file being included will be the output of another program. – supercat Nov 22 '18 at 01:44
  • @Leushenko I agree (see my comment above the one above yours). In the absence of an actually compelling use case for it, I'd mandate `include_once` semantics for all includes. I don't think it would be hard to implement. You can simply start hashing and the possibly memcmp-aring once you see two or more includes with the same size. – PSkocik Nov 22 '18 at 14:28
21

You seem to be operating under the assumption that the purpose of the "#include" feature even existing in the language is to provide support for decomposition of programs into multiple compilation units. That is incorrect.

It can perform that role, but that was not its intended purpose. C was originally developed as slightly higher-level language than PDP-11 Macro-11 Assembly for reimplementing Unix. It had a macro preprocessor because that was a feature of Macro-11. It had the ability to textually include macros from another file because that was a feature of Macro-11 that the existing Unix they were porting to their new C compiler had made use of.

Now it turns out that "#include" is useful for separating code into compilation units, as (arguably) a bit of a hack. However, the fact that this hack existed meant that it became The Way that is done in C. The fact that a way existed meant no new method ever needed to be created to provide this functionality, so nothing safer (eg: not vulnerable to multiple-inclusion) was ever created. Since it was already in C, it got copied into C++ along with most of the rest of C's syntax and idioms.

There is a proposal for giving C++ a proper module system so this 45 year old preprocessor hack can finally be dispensed with. I don't know how imminent this is though. I've been hearing about it being in the works for more than a decade.

T.E.D.
  • 41,324
  • 8
  • 64
  • 131
10

No, this would significantly hinder the options available to, for example, library writers. For example, Boost.Preprocessor allows one to use pre-processor loops, and the only way to achieve those is by multiple inclusions of the same file.

And Boost.Preprocessor is a building block for many very useful libraries.

SergeyA
  • 56,524
  • 5
  • 61
  • 116
  • 1
    It would not hinder *any* of that. OP asked about a *default* behaviour, not an unchangeable behaviour. It would be entirely sensible to change the default and instead provide a preprocessor flag `#pragma reentrant` or something along these lines. Hindsight is 20/20. – Konrad Rudolph Nov 23 '18 at 11:12
  • It would hinder it in the sense of forcing people to update their libraries and dependencies, @KonradRudolph. Not always a problem, but it could cause issues with some legacy programs. Ideally, there would also be a command-line switch to specify whether the default is `once` or `reentrant`, to mitigate this or other potential issues. – Justin Time - Reinstate Monica Nov 26 '18 at 23:18
  • 1
    @JustinTime Well as my comment says it’s clearly not a backwards compatible (and therefore feasible) change. The question, however, was why it was *initially* designed that way, not why it’s not being changed. And the answer to that is, unambiguously, that the original design was a huge mistake with far-reaching consequences. – Konrad Rudolph Nov 27 '18 at 09:51
8

In the firmware for the product I mainly work on, we need to be able to specify where functions and global/static variables should be allocated in memory. Real-time processing needs to live in L1 memory on chip so the processor can access it directly, fast. Less important processing can go in L2 memory on chip. And anything that doesn't need to be handled particularly promptly can live in the external DDR and go through caching, because it doesn't matter if it's a little slower.

The #pragma to allocate where things go is a long, non-trivial line. It'd be easy to get it wrong. The effect of getting it wrong would be that the code/data would be silently put into default (DDR) memory, and the effect of that might be closed-loop control stopping working for no reason that's easy to see.

So I use include files, which contain just that pragma. My code now looks like this.

Header file...

#ifndef HEADERFILE_H
#define HEADERFILE_H

#include "set_fast_storage.h"

/* Declare variables */

#include "set_slow_storage.h"

/* Declare functions for initialisation on startup */

#include "set_fast_storage.h"

/* Declare functions for real-time processing */

#include "set_storage_default.h"

#endif

And source...

#include "headerfile.h"

#include "set_fast_storage.h"

/* Define variables */

#include "set_slow_storage.h"

/* Define functions for initialisation on startup */

#include "set_fast_storage.h"

/* Define functions for real-time processing */

You'll notice multiple inclusions of the same file there, even just in the header. If I mistype something now, the compiler will tell me it can't find the include file "set_fat_storage.h" and I can easily fix it.

So in answer to your question, this is a real, practical example of where multiple inclusion is required.

Graham
  • 1,597
  • 6
  • 17