c++ - Why aren't my include guards preventing recursive inclusion and multiple symbol definitions? -
two common questions include guards:
first question:
why aren't include guards protecting header files mutual, recursive inclusion? keep getting errors non-existing symbols there or weirder syntax errors every time write following:
"a.h"
#ifndef a_h #define a_h #include "b.h" ... #endif // a_h
"b.h"
#ifndef b_h #define b_h #include "a.h" ... #endif // b_h
"main.cpp"
#include "a.h" int main() { ... }
why errors compiling "main.cpp"? need solve problem?
second question:
why aren't include guards preventing multiple definitions? instance, when project contains 2 files include same header, linker complains symbol being defined multiple times. instance:
"header.h"
#ifndef header_h #define header_h int f() { return 0; } #endif // header_h
"source1.cpp"
#include "header.h" ...
"source2.cpp"
#include "header.h" ...
why happening? need solve problem?
first question:
why aren't include guards protecting header files mutual, recursive inclusion?
they are.
what not helping dependencies between definitions of data structures in mutually-including headers. see means, let's start basic scenario , see why include guards mutual inclusions.
suppose mutually including a.h
, b.h
header files have trivial content, i.e. ellipses in code sections question's text replaced empty string. in situation, main.cpp
happily compile. , include guards!
if you're not convinced, try removing them:
//================================================ // a.h #include "b.h" //================================================ // b.h #include "a.h" //================================================ // main.cpp // // luck getting compile... #include "a.h" int main() { ... }
you'll notice compiler report failure when reaches inclusion depth limit. limit implementation-specific. per paragraph 16.2/6 of c++11 standard:
a #include preprocessing directive may appear in source file has been read because of #include directive in file, up implementation-defined nesting limit.
so what's going on?
- when parsing
main.cpp
, preprocessor meet directive#include "a.h"
. directive tells preprocessor process header filea.h
, take result of processing, , replace string#include "a.h"
result; - while processing
a.h
, preprocessor meet directive#include "b.h"
, , same mechanism applies: preprocessor shall process header fileb.h
, take result of processing, , replace#include
directive result; - when processing
b.h
, directive#include "a.h"
tell preprocessor processa.h
, replace directive result; - the preprocessor start parsing
a.h
again, meet#include "b.h"
directive again, , set potentially infinite recursive process. when reaching critical nesting level, compiler report error.
when include guards present, however, no infinite recursion set in step 4. let's see why:
- (same before) when parsing
main.cpp
, preprocessor meet directive#include "a.h"
. tells preprocessor process header filea.h
, take result of processing, , replace string#include "a.h"
result; - while processing
a.h
, preprocessor meet directive#ifndef a_h
. since macroa_h
has not yet been defined, keep processing following text. subsequent directive (#defines a_h
) defines macroa_h
. then, preprocessor meet directive#include "b.h"
: preprocessor shall process header fileb.h
, take result of processing, , replace#include
directive result; - when processing
b.h
, preprocessor meet directive#ifndef b_h
. since macrob_h
has not yet been defined, keep processing following text. subsequent directive (#defines b_h
) defines macrob_h
. then, directive#include "a.h"
tell preprocessor processa.h
, replace#include
directive inb.h
result of preprocessinga.h
; - the compiler start preprocessing
a.h
again, , meet#ifndef a_h
directive again. however, during previous preprocessing, macroa_h
has been defined. therefore, compiler skip following text time until matching#endif
directive found, , output of processing empty string (supposing nothing follows#endif
directive, of course). preprocessor therefore replace#include "a.h"
directive inb.h
empty string, , trace execution until replaces original#include
directive inmain.cpp
.
thus, include guards protect against mutual inclusion. however, can't dependencies between definitions of classes in mutually-including files:
//================================================ // a.h #ifndef a_h #define a_h #include "b.h" struct { }; #endif // a_h //================================================ // b.h #ifndef b_h #define b_h #include "a.h" struct b { a* pa; }; #endif // b_h //================================================ // main.cpp // // luck getting compile... #include "a.h" int main() { ... }
given above headers, main.cpp
not compile.
why happening?
to see what's going on, enough go through steps 1-4 again.
it easy see first 3 steps , of fourth step unaffected change (just read through them convinced). however, different happens @ end of step 4: after replacing #include "a.h"
directive in b.h
empty string, preprocessor start parsing content of b.h
and, in particular, definition of b
. unfortunately, definition of b
mentions class a
, has never been met before because of inclusion guards!
declaring member variable of type has not been declared is, of course, error, , compiler politely point out.
what need solve problem?
you need forward declarations.
in fact, definition of class a
not required in order define class b
, because pointer a
being declared member variable, , not object of type a
. since pointers have fixed size, compiler won't need know exact layout of a
nor compute size in order define class b
. hence, enough forward-declare class a
in b.h
, make compiler aware of existence:
//================================================ // b.h #ifndef b_h #define b_h // forward declaration of a: no need #include "a.h" struct a; struct b { a* pa; }; #endif // b_h
your main.cpp
compile. couple of remarks:
- not breaking mutual inclusion replacing
#include
directive forward declaration inb.h
enough express dependency ofb
ona
: using forward declarations whenever possible/practical considered good programming practice, because helps avoiding unnecessary inclusions, reducing overall compilation time. however, after eliminating mutual inclusion,main.cpp
have modified#include
botha.h
,b.h
(if latter needed @ all), becauseb.h
no more indirectly#include
d througha.h
; - while forward declaration of class
a
enough compiler declare pointers class (or use in other context incomplete types acceptable), dereferencing pointersa
(for instance invoke member function) or computing size illegal operations on incomplete types: if needed, full definition ofa
needs available compiler, means header file defines must included. why class definitions , implementation of member functions split header file , implementation file class (class templates exception rule): implementation files, never#include
d other files in project, can safely#include
necessary headers make definitions visible. header files, on other hand, won't#include
other header files unless need (for instance, make definition of base class visible), , use forward-declarations whenever possible/practical.
second question:
why aren't include guards preventing multiple definitions?
they are.
what not protecting multiple definitions in separate translation units. explained in this q&a on stackoverflow.
too see that, try removing include guards , compiling following, modified version of source1.cpp
(or source2.cpp
, matters):
//================================================ // source1.cpp // // luck getting compile... #include "header.h" #include "header.h" int main() { ... }
the compiler complain here f()
being redefined. that's obvious: definition being included twice! however, above source1.cpp
will compile without problems when header.h
contains proper include guards. that's expected.
still, when include guards present , compiler stop bothering error message, linker insist on fact multiple definitions being found when merging object code obtained compilation of source1.cpp
, source2.cpp
, , refuse generate executable.
why happening?
basically, each .cpp
file (the technical term in context translation unit) in project compiled separately , independently. when parsing .cpp
file, preprocessor process #include
directives , expand macro invocations encounters, , output of pure text processing given in input compiler translating object code. once compiler done producing object code 1 translation unit, proceed next one, , macro definitions have been encountered while processing previous translation unit forgotten.
in fact, compiling project n
translation units (.cpp
files) executing same program (the compiler) n
times, each time different input: different executions of same program won't share state of previous program execution(s). thus, each translation performed independently , preprocessor symbols encountered while compiling 1 translation unit not remembered when compiling other translation units (if think moment, realize desirable behavior).
therefore, though include guards preventing recursive mutual inclusions , redundant inclusions of same header in 1 translation unit, can't detect whether same definition included in different translation unit.
yet, when merging object code generated compilation of .cpp
files of project, linker will see same symbol defined more once, , since violates one definition rule. per paragraph 3.2/3 of c++11 standard:
every program shall contain 1 definition of every non-inline function or variable odr-used in program; no diagnostic required. definition can appear explicitly in program, can found in standard or user-defined library, or (when appropriate) implicitly defined (see 12.1, 12.4 , 12.8). an inline function shall defined in every translation unit in odr-used.
hence, linker emit error , refuse generate executable of program.
what need solve problem?
if want keep function definition in header file #include
d multiple translation units (notice, no problem arise if header #include
d one translation unit), need use inline
keyword.
otherwise, need keep declaration of function in header.h
, putting definition (body) one separate .cpp
file (this classical approach).
the inline
keyword represents non-binding request compiler inline function's body directly @ call site, rather setting stack frame regular function call. although compiler doesn't have fulfill request, inline
keyword succeed in telling linker tolerate multiple symbol definitions. according paragraph 3.2/5 of c++11 standard:
there can more 1 definition of a class type (clause 9), enumeration type (7.2), inline function external linkage (7.1.2), class template (clause 14), non-static function template (14.5.6), static data member of class template (14.5.1.3), member function of class template (14.5.1.1), or template specialization template parameters not specified (14.7, 14.5.5) in program provided each definition appears in different translation unit, , provided definitions satisfy following requirements [...]
the above paragraph lists definitions commonly put in header files, because can safely included in multiple translation units. other definitions external linkage, instead, belong in source files.
using static
keyword instead of inline
keyword results in suppressing linker errors giving function internal linkage, making each translation unit hold private copy of function (and of local static variables). however, results in larger executable, , use of inline
should preferred in general.
an alternative way of achieving same result static
keyword put function f()
in unnamed namespace. per paragraph 3.5/4 of c++11 standard:
an unnamed namespace or namespace declared directly or indirectly within unnamed namespace has internal linkage. other namespaces have external linkage. name having namespace scope has not been given internal linkage above has same linkage enclosing namespace if name of:
— variable; or
— a function; or
— named class (clause 9), or unnamed class defined in typedef declaration in class has typedef name linkage purposes (7.1.3); or
— named enumeration (7.2), or unnamed enumeration defined in typedef declaration in enumeration has typedef name linkage purposes (7.1.3); or
— enumerator belonging enumeration linkage; or
— template.
for same reason mentioned above, inline
keyword should preferred.
Comments
Post a Comment