Why isn't C/C++'s "#pragma once" an ISO standard?

C++CPragmaInclude Guards

C++ Problem Overview


I am currently working on a big project and maintaining all those include guards makes me crazy! Writing it by hand is frustrating waste of time. Although many editors can generate include guards this doesn't help much:

  1. Editor generates guard symbol based on a filename. The problem occurs when you have headers with the same filename in different directories. Both of them will get the same include guard. Including directory structure into the guard symbol would require some fancy approach from the editor, since slashes and backslashes in the macro are not the best thing.

  2. When I have to rename a file I should rename all the include guards as well (in the ifndef, define and ideally endif's comment). Annoying.

  3. Preprocessor is flooded with tons of symbols without a clue what they mean.

  4. Nevertheless definition is included once, compiler still opens header every time it meets header inclusion.

  5. Include guards don't fit into namespaces nor templates. In fact they are subverting namespaces!

  6. You have a chance that your guard symbol won't be unique.

Maybe they were acceptable solution in times when programs contained less than 1000 headers in single directory. But nowadays? It is ancient, it has nothing to do with modern coding habits. What bothers me the most is that this issues could be almost compeletly solved by #pragma once directive. Why is it not a standard?

C++ Solutions


Solution 1 - C++

A directive like #pragma once is not trivial to define in a fully portable way that has clear an unambiguous benefits. Some of the concepts for which it raises questions are not well defined on all systems that support C, and defining it in a simple way might provide no benefit over conventional include guards.

When the compile encounters #pragma once, how should it identify this file so that it doesn't include its contents again?

The obvious answer is the unique location of the file on the system. This is fine if the system has unique locations for all files but many systems provide links (symlinks and hardlinks) that mean that a 'file' doesn't have a unique location. Should the file be re-included just because it was found via a different name? Probably not.

But now there is a problem, how is it possible to define the behaviour of #pragma once in a way that has an exact meaning on all platforms - even those that don't even have directories, let alone symlinks - and still get the desirable behaviour on systems that do have them?

You could say that a files identity is determined by its contents, so if an included file has a #pragma once and a file is included that has exactly the same contents, then the second and subsequent #includes shall have no effect.

This is easy to define and has well defined semantics. It also has good properties such that if a project is moved from a system that supports and uses filesystem links to one that doesn't, it still behaves the same.

On the downside, every time an include file is encountered containing a #pragma once its contents must be checked against every other file using #pragma once that has already been included so far. This implies a performance hit similar to using #include guards in any case and adds a not insignificant burden to compiler writers. Obviously, the results of this could be cached, but the same is true for conventional include guards.

Conventional include guards force the programmer to choose a macro that is the unique identifier for an include file, but at least the behaviour is well-defined and simple to implement.

Given the potential pitfalls and costs, and the fact the conventional include guards do work, it is not surprising to me that the standards committee didn't feel the need to standardize #pragma once.

Solution 2 - C++

Include guards are definitely an annoyance, and C should have originally been designed such that headers would be included once by default - requiring some special option to include a header multiple times.

However, it wasn't ,and you're mostly stuck with having to use include guards. That said, #pragma once is pretty widely supported so you might be able to get away with using it.

Personally, I solve your 1st problem (similarly named include files) by adding a GUID to the include guard. It's ugly, and most people hate it (so I'm often forced to not use it at work), but your experience shows that the idea has some value - even if it is hideously ugly (but then again the whole include guard thing is kind of a hack - why not go whole hog?):

#ifndef C_ASSERT_H_3803b949_b422_4377_8713_ce606f29d546
#define C_ASSERT_H_3803b949_b422_4377_8713_ce606f29d546

// blah blah blah...

#endif

I've heard that compilers don't actually reopen header files that have include guards (they've learned to recognize the idiom). I'm not sure if that's true (or to what extent it's true); I've never measured it. I also don't worry about it, but my projects aren't so huge that it's a problem.

My GUID hack pretty much solves items 1, 5, and 6. I just live with items 2, 3, and 4. Actually, for item 2 you could live without renaming the include guard macro when you rename the file as the GUID will ensure it remains unique. In fact, there's no reason to incorporate the filename at all with the GUID. But I do - tradition, I suppose.

Solution 3 - C++

  1. How often do you have to add an include file to this project? Is it really so hard to add DIRNAME_FILENAME to the guard? And there is always GUIDs.

  2. Do you really rename files that often? Ever? Also, putting the GUARD in the #endif is just as annoying as any other useless comment.

  3. I doubt your 1000 header files guard defines are even a small percentage of the number of defines generated by your system libraries (especially on Windows).

  4. I think MSC 10 for DOS (20+ years ago) kept track of what headers were included and if they contained guards would skip them if included again. This is old tech.

  5. namespaces and templates should not span headers. Dear me, don't tell me you do this:

    template <typename foo>
    class bar {
    #include "bar_impl.h"
    };
    
  6. You said that already.

Solution 4 - C++

As was already noted, C++ Standard should account for different development platforms some of which may have limitations making #pragma once support impossible to implement.

On the other hand, support for threads was not added for the similar reason previously, but newer C++ Standard includes threads nevertheless. And in the latter case we may cross-compile for a very limited platform, but development is done on a full-fledged platform. Since GCC supports this extension, I think, the real answer to your question is that there is no interested party in pushing this feature into C++ Standard.

From practical standpoint, include guards caused our team more trouble than the incompliance of #pragma once directive. For instance, GUID in include guards do not help in case if file is duplicated and later both copies are included. When using only #pragma once we get duplicate definition error and can spend time unifying source code. But in case of include guards the problem may require run-time testing to catch, e.g. this happens if copies differ in default arguments for function parameters.

I avoid using include guards. If I have to port my code to a compiler without #pragma once support, I will write a script which will add include guards to all header files.

Solution 5 - C++

> The problem occurs when you have headers with the same filename in different directories.

So you have two headers which are both called ice_cream_maker.h in your project, both of which have a class called ice_cream_maker defined in them which performs the same function? Or are you calling every class in your system foo?

> Nevertheless definition is included once, compiler still opens header every time it meets header inclusion.

Edit the code so you don't include headers multiple times.

For dependent headers (rather than the main header for a library), I often use header guards which are like this:

#ifdef FOO_BAR_BAZ_H
#error foo_bar_baz.h multiply included
#else
#define FOO_BAR_BAZ_H

// header body

#endif

Solution 6 - C++

IIRC, #pragma anything is not part of the language. And it shows up in practice, a lot.

(edit:completely agree that the inclusion and linking system should have been more of a focus for the new standard as it is one of the most obvious weaknesses, in 'le this day and age' )

Solution 7 - C++

You can probably avoid name collisions without resorting to random strings by setting the include guard to contain the name of the class and namespace that's in the file.

Besides, #pragma once is supported by both MS compilers and GCC for quite some time now so why does it bother you that it's not on the ISO standard?

Solution 8 - C++

A pragmatic solution:

  1. choose some consistent guard-naming policy (e.g. path relative to project root + file name, or whatever else you choose). Include possible exceptions for third-party code.

  2. write a program (a simple python script would do) to walk recursively the source tree and verify that the guards all conformal to the policy. And whenever the guards are wrong, output a diff (or sed script, or whatever else) that the user can easily apply to fix. Or just ask for a confirmation and make changes from the same program.

  3. make everyone on the project use it (say, before submitting to source control).

Solution 9 - C++

I think right way to allow to do multiple include with special pragma only and disallow to multiple include by default for example:

#pragma allow_multiple_include_this_file

So since you asked why. Did you send your proposal to standart developers? :) I'm not send too. Can one be a reason?

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmipView Question on Stackoverflow
Solution 1 - C++CB BaileyView Answer on Stackoverflow
Solution 2 - C++Michael BurrView Answer on Stackoverflow
Solution 3 - C++jmucchielloView Answer on Stackoverflow
Solution 4 - C++Vyacheslav LanovetsView Answer on Stackoverflow
Solution 5 - C++Pete KirkhamView Answer on Stackoverflow
Solution 6 - C++rama-jka totiView Answer on Stackoverflow
Solution 7 - C++shooshView Answer on Stackoverflow
Solution 8 - C++anonymousView Answer on Stackoverflow
Solution 9 - C++Maxim KholyavkinView Answer on Stackoverflow