views:

304

answers:

7

I have a project which has the following directory structure.

root
--include
----module1
----module2
--src
----module1
----module2

So a file say foo.cpp in src/module1 has to include like,

#include "../../include/module1/foo.hpp"

This looks messy and tough to write. I found writing include like

#include <module1/foo.h>

and providing include file search path to root/include when compiling looks neat. However, I am not sure that this style has got any drawbacks.

Which one do you prefer and why? Also do you see any problems in organizing files in the above way?

+1  A: 

I much prefer the 2nd way

#include <module1/foo.h>

I find it makes the source much easier to look at. The problem is, when other people come look at your code its not necessarily inherent where the header file is located whereas the first way of including is.

silent1mezzo
+1  A: 

Every style has some drawbacks, but I prefer the one you specified. The include path must not contain any upward relativity (such as ../..) and must specify the module it relies on.

alemjerus
+3  A: 
#include "../../include/module1/foo.hpp"

Specifying paths should be avoided as much as possible. Compilers provide you with a cleaner alternative to achieve the same. Further, a clean design should see to it that you do not need to juggle relative paths for including headers.

A better idea of which one to use (including whether to use quotes or the angle-brackets) can be had from the standard.

From my copy of the C++ draft:

16.2 Source file inclusion

2 A preprocessing directive of the form

#include <h-char-sequence> new-line`

searches a sequence of implementation-defined places for a header identified uniquely by the specified sequence between the < and > delimiters, and causes the replacement of that directive by the entire contents of the header. How the places are specified or the header identified is implementation-defined.

3 A preprocessing directive of the form

# include "q-char-sequence" new-line 

causes the replacement of that directive by the entire contents of the source file identified by the specified sequence between the " delimiters. The named source file is searched for in an implementation-defined manner. If this search is not supported, or if the search fails, the directive is reprocessed as if it read

#include <h-char-sequence> new-line`

with the identical contained sequence (including > characters, if any) from the original directive.

7 Although an implementation may provide a mechanism for making arbitrary source files available to the < > search, in general programmers should use the < > form for headers provided with the implementation, and the " " form for sources outside the control of the implementation.

dirkgently
Any tips on how to avoid creating "backtracking" (..) in the header specifiers?
Skurmedel
@Skurmedel: Do you mean relative paths that point to headers declared at an ancestral level? I am tempted to think the compiler's header include option and/or a proper separation of concerns always fixed those. Except, of course, in a few, very, very rare cases. E.g: Legacy code -- where you just can't change the module design even if you want to.
dirkgently
Yeah. If I do include <stuff_defined_in_a_level_below.hpp>, wouldn't I have to tell the compiler to use that include path? It is of course a compiler specific thing, but in general? (You can tell that I suck when it comes to managing my headers :))
Skurmedel
Oh, now I see what you mean. It seems my problem is that I don't separate the include and source folder, I mostly have them merged. Time to stop with that habit.
Skurmedel
@Skurmedel: Yes, that's a Good Habbit (TM)!
dirkgently
Thanks for the help. I can only upvote once sadly.
Skurmedel
+1  A: 

As a small refinement, I suggest you allow cc files in module1 to access their .h files directly:

module1/%.cc: -I $ROOT/includes/module1

or similar. This will create a visual effect in your c files that distinguishes foreign includes from the default include:

// module1/abc.cc
#include <abc.h>
#include <module2/def.h>
Alex Brown
+1  A: 

Google's C++ style guide has a section on this where they discuss ordering and naming of includes. It basically agrees with what has been said in the other answers so far, but is worth having a look as it is very specific.

I prefer (and Google agrees) to not have relative paths using .. in the include directives. Your initial assessment that it looks neat is correct. Having long, clunky, relative paths makes things harder to read and harder to refactor. I think your approach is correct.

As far as separating source and include files into two different sub-trees: why not have the headers live next to the source files? It makes it easier to match them. Unless you expect other projects to be using your headers and just linking to your binaries, I guess.
<shrug/>

jeffamaphone
Other projects reusing your code is difficult to know in advance, so separation is better I think :)
Matthieu M.
+2  A: 

I support both styles... for different uses

Let's suppose you also have a directory root/src/common for the purpose of my example

// in src/module1/foo.cpp
#include "module1/foo.h"

#include "../common/stringProcessing.h"

Include

I prefer not to see the 'include' directory, as said of course it is thus more difficult to locate the exact header file... but when you begin and move toward multiple independent libraries you NEED to abstract because you want to be able to move the various components around without altering the code, and I am all for consistency.

Also, there is always the risk using '..' that it does not go where you thought because of a symbolic link traversed backward :/

Source

Sometimes you have headers that are not public, and thus not in the include directory. These are usually for implementation details that are not relevant for your clients. For those I use the .. if need be and precise the exact location.

This allows: - not to clutter the -I with all possible directories of src - locate easily the file among your sources - easily test for dependencies among your sources (grep for ..)

Misc

If I have to type

#include "module/foo.h"

Then I expect to use:

module::Foo myClass;

which makes it easy to match one particular type to one particular module.

The requirement one library - one namespace, with identical names, makes it easy to navigate the some ~300 or ~400 components we have at work: we HAD to work out some way to organize them!

This means though that your initial layout is reworked as (for the module project):

root
-- include
---- module
------ part1
------ part2
-- src
---- part1
---- part2

And you then use the following directive: -I/path../root/include And I expect you to create either a libmodule.so library or a module binary.

Matthieu M.
+1  A: 

I don't use paths in #include directives. In my experience, they always cause problems in the maintenance phase. Many compilers allow you to specify search tree for header files.

Files Can Move

They will. Your hierarchy will change. You may want to put the files on a different drive. They may change location when another user modifies your code. The files may move when they are ported for another project. There is nothing preventing your files from moving.

Moving Files Without Modifying Them

Knowing that files will move, if the path is in the file, the file must be modified when it is moved. Without the path in the file, only the build instructions need to change. With large projects, modifying many files is a pain (even with scripts).

In my opinion, paths in #include directives are evil. People who do this should be retrained or sent to other companies.

Thomas Matthews
And if, let's say, you develop a string substitute and still want to use std::map? Paths in headers are essential. But some people tend to avoid using knives, because they can kill.
alemjerus
Paths in `#include` are not essential. I have been working on my own project for many years and none of the `#include` have paths. I have the compiler set up to search many directories. In most build procedures, you can state per file directories to be searched. The common command line parameter is `-I`. I even have `std::map` of `wxString`. Neither the #include for `<map>` nor the wxWidgets header for `wxString` require paths in the `#include`.
Thomas Matthews