While using header files, each header file should be included only once.
For example, lets say I have three classes. class A, class B and class C.
class A is declared in the file A.h, class B is declared in the file B.h and class C is declared in the file C.h, and they are defined in their respective .cpp files.
A.cpp
#include "A.h"
class A
{
}
In the B.cpp file, the following will be the definition of the class.
#include "A.h"
#include "B.h"
class B
{
A a;
}
And same goes for the C.cpp file as well.
#include "A.h"
#include "B.h"
#include "C.h"
class C
{
A a;
B b;
}
Now, if include guards are not written in the header files, then the g++ compiler will throw an error.
My question is, why should we need to specify the include guards? Is it not common sense that each header file should be included only once? Why doesn't the compiler take care of multiple includes by itself?
My question is, why should we need to specify the include guards? Is it not common sense that each header file should be included only once? Why doesn't the compiler take care of multiple includes by itself?
Because it's not true for all headers. One example of a header that can be included more than once, and being able to do so actually matters, is the <assert> header.
There's not really any point to trying to fix up the header system of copying and pasting file contents. Really we just need to move to a better build model.
As its been said, sometimes you do want an include file to be invoked more than once; and there may be many situations where this is desirable.
One example where this comes in useful is for optimizing instantiations of large, complicated templates. Consider some typical large, complicated template class
template<typename T> class ComplicatedTemplate {
// ... Boring stuff goes here
};
Having this large template instantiated and compiled in every translation unit, for the same template type, over and over again, gets really old. It slows down a compile, and needlessly bloats every object module, only to have the linker deal with stripping out a ton of duplicate template instantiations. It's a lot of wasted work.
Many compilers offer ways to control template instantiations. The exact details can vary, sometimes, but I'll use the typical approach, as used by gcc, which you can read about here:
https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html
Say, you'd want to burn some CPU time instantiating ComplicatedTemplate<std::vector<int>>, ComplicatedTemplate<std::vector<char>>, maybe ComplicatedTemplate<std::string<std::string>> in some translation unit called "complicated.cpp", and just declare them extern in the header file.
Ok, so you end up with this in complicated_template.H
template<typename T> class ComplicatedTemplate {
// ... Boring stuff goes here
};
extern template ComplicatedTemplate<std::vector<int>>;
extern template ComplicatedTemplate<std::vector<char>>;
extern template ComplicatedTemplate<std::vector<std::string>>;
Then, in complicated.cpp:
#include "complicated_template.H"
template ComplicatedTemplate<std::vector<int>>;
template ComplicatedTemplate<std::vector<char>>;
template ComplicatedTemplate<std::vector<std::string>>;
Ok, so that's going to work fine, except for one inconvenience. If you decide to also add ComplicatedTemplate<std::vector<SomeCustomType>>, or anything else, to the list of pre-instantiated templates, this needs to be done in two places; both in the header file, and in complicated.cpp
Here's a typical approach that eliminates this duplication:
complicated_template.H:
template<typename T> class ComplicatedTemplate {
// ... Boring stuff goes here
};
#include "complicated_template_inst.H"
complicated_template_inst.H:
#ifndef EXTERN
#define EXTERN
#endif
EXTERN template ComplicatedTemplate<std::vector<int>>;
EXTERN template ComplicatedTemplate<std::vector<char>>;
EXTERN template ComplicatedTemplate<std::vector<std::string>>;
Then, in complicated.cpp:
#include "complicated_template.H"
#define EXTERN
#include "complicated_template_inst.H"
Now, the list of template instances that get pre-instantiated is in one place. Using the earlier example, adding:
EXTERN template ComplicatedTemplate<std::vector<SomeCustomType>>;
has the effect of both preventing the wasted instantiation of this template in every translation unit that needs that template instance, and of instantiating it explicitly in the complicated.cpp translation unit.
You will see this kind of an approach in many large C++ libraries. They will typically define their templates, then pre-instantiating them by pulling in a separate #include file, that contains some preprocessor-fu. The actual shared library will also include the second file a second time, after pulling in the externally-visible header file, with the preprocessor accordingly rigged up to turn those extern template declarations into template instantiations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With