2

I'm creating an header-only C++11/14 library and I'm not sure on how I should handle #include directives between library files.

Should I try to group as many #include directives as possible in the user-oriented module header file or should internal files include files they require (sometimes repeating the same includes)?


Approach A:

In this approach, the module header file includes all required dependencies and then includes the implementations. The implementations' header files do not include anything by themselves.

// Library/Module/Module.hpp
// This file is intended to be included by the user in his projects.

#ifndef MODULE
#define MODULE

#include <vector>
#include "Library/Module/Impl/SharedDependency.hpp"
#include "Library/Module/Impl/Class1.hpp"
#include "Library/Module/Impl/Class2.hpp"

#endif MODULE

-

// Library/Module/Impl/SharedDependency.hpp

#ifndef SHARED_DEPENDENCY
#define SHARED_DEPENDENCY

inline void sharedFunc() { }

#endif

-

// Library/Module/Impl/Class1.hpp

#ifndef CLASS1
#define CLASS1

// No need to include "SharedDependency.hpp", as it will be included by
// the module header file. Same applies for <vector>.
struct Class1 
{ 
    std::vector<int> v;        
    Class1() { sharedFunc(); } 
};

#endif

-

// Library/Module/Impl/Class2.hpp

#ifndef CLASS2
#define CLASS2

// No need to include "SharedDependency.hpp", as it will be included by
// the module header file. Same applies for <vector>.
struct Class2
{ 
    std::vector<int> v;        
    Class2() { sharedFunc(); } 
};

#endif


Approach B:

In this approach, the module header file includes only the implementation headers. If the implementation headers require additional includes, they include the files themselves (recursively), sometimes repeating the same include.

// Library/Module/Module.hpp
// This file is intended to be included by the user in his projects.

#ifndef MODULE
#define MODULE

#include "Library/Module/Impl/Class1.hpp"
#include "Library/Module/Impl/Class2.hpp"

#endif MODULE

-

// Library/Module/Impl/SharedDependency.hpp

#ifndef SHARED_DEPENDENCY
#define SHARED_DEPENDENCY

inline void sharedFunc() { }

#endif

-

// Library/Module/Impl/Class1.hpp

#ifndef CLASS1
#define CLASS1

#include <vector>
#include "Library/Module/Impl/SharedDependency.hpp"

struct Class1
{ 
    std::vector<int> v;        
    Class1() { sharedFunc(); } 
};

#endif

-

// Library/Module/Impl/Class2.hpp

#ifndef CLASS2
#define CLASS2

#include <vector>
#include "Library/Module/Impl/SharedDependency.hpp"

struct Class2
{ 
    std::vector<int> v;        
    Class2() { sharedFunc(); } 
};

#endif

What is the best approach?

Intuitively, I think Approach A is the best, as it avoids repeating the same includes and makes clear what files need to be included before the other files. The biggest drawback is, though, that syntax highlighting stops working in my IDE (QT-Creator), in the implementation files with no include directives.


EDIT:

This question was voted to be closed for the reason "opinion based". I disagree, because in a large header-only project such as my library including files may take a lot of compile time. Therefore, approach A may be faster than approach B, or the opposite.

Sam
  • 7,252
  • 16
  • 46
  • 65
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Personally I want to avoid doing the compiler's job of recognizing duplicate includes. Also headers become easier to test when they are self-reliant, again reducing development time. But favoring that is just my personal opinion. If you could define your "best"-ness criterion then there you would have your answer. So essentially you are asking others to define what "best" should mean for you. – Cheers and hth. - Alf May 20 '14 at 16:48
  • Repeating Alf's advice, every file should include all the headers it depends on. This will allow your users fine grained control over pulling in a specific part of your library if they desire to, rather than including the *convenience* header `module.hpp`. And the last edit about *my large library*, *includes take time* etc. is meaningless unless you provide some proof. – Praetorian May 20 '14 at 17:33
  • in case this doesn't get reopened: as Alf and Praetorian advised, I also strongly prefer case B because it becomes much easier to verify a sane build process (e.g. CMake has a standalone header test macro). The module header for end-users is good idea of course, but you shouldn't worry too much about duplicate includes with modern compilers. – TemplateRex May 20 '14 at 18:50
  • With header guards and `#pragma once` I would be confident that additional includes of the same file don't waste more than a few milliseconds of compile time. – Casey May 20 '14 at 20:11

1 Answers1

0

Approach B is actually the best approach, since including the same header multiple times does not produce any observable compilation time increase, but is advantageous for the following reasons:

  • Modern IDEs can use libclang or proprietary solutions to parse the #include directives and provide code-aware syntax highlighting and autocompletion features.

  • As mentioned by TemplateRex, it becomes much easier to verify a sane build process. CMake, for example, provides macros that automatically generate a test for every header.

  • As mentioned by Alf, it is good practice to have every file include all the headers it depends on - users of the library can then "cherry-pick" header files they require, instead of being unexpectedly force to manually include a parent header.

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416