Avoiding double inclusion: Preprocessor directive vs. makefiles
Asked Answered
C

4

6

I'm working on moving from frankenstein and one-file thousands-of-lines programs to well structured and organized, multi-file programs. Right now what seems to be natural (naively) is to make a love-triangle of header inclusions for three of my files:
file_1 includes file_2, file_4
file_2 includes file_3, file_4
file_3 includes file_1 .... etc etc
These files have variables, methods, structs, etc that I need between other files.

And of course I'm getting double inclusion errors.

My question: should I avoid these problems by using preprocessor directives in headers (e.g. including structs, methods, etc. entirely in the header), or should I be compiling using a makefile (which I hear can also be used to resolve this problem---but I've never made one)?

Cuomo answered 15/2, 2011 at 7:6 Comment(1)
Break cycles by using forward declarationsSenarmontite
A
4

Preprocessor directives and headers containing declaration and shared structures etc is the way to go. Makefiles just helps to compile sources and to link object files to external libraries to form the final binary, it won't help to resolve the multiple-inclusion issue. In a nutshell, declare the following in a header file:

  • structs
  • shared variable as extern (and define it in one of the .c files)
  • method declaration (and define the methods in one of the .c files)

protect them with #IFNDEF and #ENDIF, then include the header file into the various .c files...

Advection answered 15/2, 2011 at 7:28 Comment(2)
In regards to shared variables: if I declare some variable in a header file, and want to use it in numerous other files, why declare it as 'extern'? So far I would just declare it as a const, and include the header...Cuomo
The preprocessor totally substitutes the contents of a file (say .h) into the file doing the include (.c file), then compiler does its thing on the final file. If you just declare it in a header and include it in multiple .c files you'll end up having each compiled .o file having its own copy of the variable which doesn't make it shared. If you're talking about a constant, then sure, not declaring extern should be okay. btw i need to verify this again, haven't touched C for a while...Advection
R
13

You should always use include guards so that you can include your common header files whenever needed. This is really independent of Makefile or whatever build tool you choose to use.

You should also try and avoid circular dependencies if possible, otherwise you will need to use forward declarations to resolve them.

Recoil answered 15/2, 2011 at 7:9 Comment(5)
No. You use forward declarations to break cycles.Senarmontite
@Martin York: what's the difference between a "circular dependency" and a "cycle" ? I consider them synonyms, with "circular dependency" being the less ambiguous term.Jankell
Okay, so If added include guards to all relevant header files, and made sure that each file that needs them is including them.... But now i'm getting compilation errors (which are resolved by compiling the files separately, i.e. g++ -c included.cc, g++ -c Program.cc, g++ -o Program Program.o included.o Undefined symbols: "initOutFile(__sFILE*&, char const*, char const*, char const*)", referenced from: initialize(char*) in cccHyQZI.o ld: symbol(s) not found collect2: ld returned 1 exit statusCuomo
This is a linker error: the compilation succeeded but it emitted symbols that the linker wasn't able to find anywhere. Did you specify all your .cc files in the compiler command line?Recoil
No I did not... :( ---Got it to work, thanks for your help sjr!Cuomo
A
4

Preprocessor directives and headers containing declaration and shared structures etc is the way to go. Makefiles just helps to compile sources and to link object files to external libraries to form the final binary, it won't help to resolve the multiple-inclusion issue. In a nutshell, declare the following in a header file:

  • structs
  • shared variable as extern (and define it in one of the .c files)
  • method declaration (and define the methods in one of the .c files)

protect them with #IFNDEF and #ENDIF, then include the header file into the various .c files...

Advection answered 15/2, 2011 at 7:28 Comment(2)
In regards to shared variables: if I declare some variable in a header file, and want to use it in numerous other files, why declare it as 'extern'? So far I would just declare it as a const, and include the header...Cuomo
The preprocessor totally substitutes the contents of a file (say .h) into the file doing the include (.c file), then compiler does its thing on the final file. If you just declare it in a header and include it in multiple .c files you'll end up having each compiled .o file having its own copy of the variable which doesn't make it shared. If you're talking about a constant, then sure, not declaring extern should be okay. btw i need to verify this again, haven't touched C for a while...Advection
C
4

An example of an include guard for "inv_tree.h" would be

#ifndef INV_TREE_H
#define INV_TREE_H
...
#endif

By surrounding the contents of "inv_tree.h" with the guard it checks weather INV_TREE_H was defined before including "inv_tree.h" If it wasn't defined it defines it and includes "inv_tree.h" otherwise it does not include it

Centralization answered 15/2, 2011 at 7:29 Comment(1)
Simplistic guards like this are only useful for toy projects. Learn to generate guids or use a guard that is guaranteed to be unique (includes company/project/namespace info)Senarmontite
S
1

header files

A header file should only include header files that are absolutely essential. This means if you derive from a class or explicitly use a class as a member, from another header file.

If you only use a reference or a pointer to a class then only forward declare it(don't include the heder file).

In this way you can break cyclic header inclusion.

Note: you should always use header guards as a header may be included via multiple paths that are not obvious to the user of your head files.

Senarmontite answered 15/2, 2011 at 8:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.