CS 70

Include Guards

  • Duck speaking

    Finally, I’ll get an answer to my question! What is an include guard?!

We need to prevent multiple declarations if a header file happens to be included multiple times.

You might think you need something like, #include-unless-already-included "constants.hpp", but the designers of C realized they didn’t need a new kind of #include, because they could use some other cool features of the preprocessor to do everything they needed.

This coding idiom is known as an include guard.

It’s probably best to just show you the include-guard idiom and then explain it. Here is constants.hpp again with an include guard:

#ifndef CONSTANTS_HPP_INCLUDED
#define CONSTANTS_HPP_INCLUDED

constexpr double PI = 3.14159265358979;

#endif
  • Alien speaking

    What is this arcane and primitive incantation??

  • Bjarne speaking

    It is part of our heritage.

  • LHS Cow speaking

    Yes, it does look a bit convoluted.

  • RHS Cow speaking

    And it’s okay that it doesn’t make sense yet. It contains preprocessor directives that we haven’t mentioned and even if you do know about them, it’s some pretty strange logic.

New Preprocessor Directives

Preprocessor Macros

#define MACRO_NAME replacement text creates a text-substitution macro for the preprocessor.

It’s basically a search-and-replace instruction—wherever MACRO_NAME appears in a file (as a distinct word, not inside a string), it will be replaced with replacement text before compilation.

  • Duck speaking

    Why do we SHOUT MACRO_NAME (in all caps)?

  • LHS Cow speaking

    It is a common convention to name preprocessor macros in all caps. That way they are distinguished from our regular variables!

  • Hedgehog speaking

    But we use all caps for constants, too.

  • Bjarne speaking

    That’s because originally people also used preprocessor macros for constants (e.g., #define PI 3.14159265358979), and C programmers still do. But in C++, we now use constexpr.

  • Cat speaking

    Just to make sure I’m clear, with that preprocessor define, if we say

    degrees = 2 * PI * radians;
    

    the preprocessor changes the code so the compiler itself sees

    degrees = 2 * 3.14159265358979 * radians;
    
  • LHS Cow speaking

    Yes, simple macros are just like like a search and replace.

Returning to our include-guard example, we had

#define CONSTANTS_HPP_INCLUDED

which specifies that replacement text is nothing at all. Technically that means “whenever you see CONSTANTS_HPP_INCLUDED in the program code, replace it with nothing”.

In our example it doesn't really matter what we replace that string with, because we’re not actually using it for textual search-and-replace at all, and we won’t ever be writing this weird word in our code.

We are just going use the fact that it was defined at all. And when we read the include file for the first time, it hasn’t been defined yet.

Conditional Code

The C preprocessor also provides a mechanism to prevent the compiler ever seeing some of the code in a file. The #if, #ifdef, and #ifndef directives control this feature, known as conditional compilation.

When we say

#ifndef CONSTANTS_HPP_INCLUDED

...stuff...

#endif`
  • If the condition is true, then ...stuff... will be processed by the preprocessor (and thus eventually seen by the compiler).
  • If the condition is false, then ...stuff... will be skipped by the preprocessor (and thus never seen by the compiler, as if it wasn’t there).

In this case the condition is “if not defined.”

  • Cat speaking

    So the text between #ifndef and #endif is retained if CONSTANTS_HPP_INCLUDED is not defined and removed if CONSTANTS_HPP_INCLUDED has been defined?

  • LHS Cow speaking

    You got it!

Include Guard Logic

  • Horse speaking

    Okay. That makes sense, I guess. But I still don’t see why it prevents multiple declarations.

  • LHS Cow speaking

    It’s probably easiest if we just walk through the logic step-by-step. Check out the video!

  • Duck speaking

    I’m sure I’ve got it, so I’m gonna skip the video.

  • LHS Cow speaking

    You do you.

Include Guard Takeaways

The main things to know about include guards are

  • In our header files, Include guards look like
#ifndef SOMEHEADER_HPP_INCLUDED
#define SOMEHEADER_HPP_INCLUDED

// Actual content of the file

#endif
  • Include guards prevent multiple definitions when a header file is included twice.
  • Every header file should have an include guard.
  • In CS 70, for a header file filename.hpp, name the macro FILENAME_HPP_INCLUDED.
    • Note that a common error is to make a new header file based on an existing file and forget to change the name in the include guard macro!
  • Hedgehog speaking

    Is that all I need? What about all the logic of how it works?

  • LHS Cow speaking

    It helps to understand the logic, but it’s also okay to just memorize the pattern so you can apply it to your own header files.

  • Duck speaking

    Do I have to name the macro WHATEVER_HPP_INCLUDED?

  • LHS Cow speaking

    Technically, you can name it whatever you want! The macro just needs to be undefined the first time the file is included, and a word that won’t be used elsewhere in your code (since it will be replaced with nothing if it appears).

  • Duck speaking

    So the macro name for each include file needs to be unique.

  • LHS Cow speaking

    Yes. It would be bad if a macro of the same name were defined in some other file, preventing the contents of our header file from being included at all!

  • RHS Cow speaking

    That’s why we base the macro name on the filename.

(When logged in, completion status appears here.)