#include <the_usual>

Recently on Freenode channel ##cpp, I saw some code using an include-all-you-can header. The idea was to help beginners to the language, help them start programming without having to remember which header was which, and which headers are needed.

Is that really a good idea?

Well, maybe. It does simplify things:

#include <usual.hpp>


int main()
 {
  return 0;
 }

But what do we put in this header? IO-stuff, certainly. Math? Maybe. A few basic containers? That’d help. std::swap and std::max? Sure, why not!

#ifndef __MODULE_USUAL__
#define __MODULE_USUAL__

// type-safe programming
#include <cstddef> // size_t, ptrdiff_t, etc.
#include <cstdint> // int64_t, etc.
#include <limits> // std::numeric_limits<T>::min, etc.

// io
#include <iostream>
#include <iomanip>
#include <fstream>

// math
#include <cmath> // may be borked on Visual Studio

// basic types & utilities
#include <utility> // swap, pair, etc.
#include <algorithm> // max, min, etc.

// usual types used in ordinary programs
#include <string>
#include <vector>
#include <list>
#include <map>

// exceptions && exception handling
#include <exception>
#include <stdexcept>

#endif
// __MODULE_USUAL__

So if we gather categories:

  • Type-safe programming. We all know that one of C and C++’s objective is to help the programmer write efficient code for the machine. This, however, comes with an important drawback for portability: int may not be the same size from one platform. It is therefore important to use the cromulent types such as size_t and std::ios::streamoff.
  • IO. Basic console and file IO are common.
  • Math. While there’s a lot more to math than std::sqrt (such as managing bits in floating point values), the basic C-inherited math header suffices for most of the cases. (However, I’ve encountered problems with Visual Studio where you have to do some magic to get things right.)
  • Utilities. Pairs, min, max, swap, are things we use a lot.
  • Containers. Are strings containers? The usual containers are std::vector, std::list, and std::map. Sure, I use many more, but not as frequently.
  • Exceptions. Exceptions are often maligned, but if you understand how to use them, they may simplify code greatly.

It seems to cover the basic needs.

*
* *

Including the usual headers may be simpler, but there are drawbacks.

The first one is compile time. A quick check reveals that the empty main program including <usual> is about 95000 lines long. Indeed:

> cpp -Wall -Wextra -std=c++11 -I. usual.cpp  | wc -l
94024

Fortunately, the order in which the includes are made isn’t that important: the include guard mechanism ensures that each is included only once, in dependency order. You still get 100K lines of code, though. However, I do not really worry about that: I can still get a bigger CPU, and good makefiles will prevent your whole project from recompiling from zero every time.

The second drawback is more insidious, and bugs me far more. True, it’s much simpler to include just one header, but you lose a lot of information. You do not know the intend of the programmer, you can’t know exactly what’s in there. If a header file includes only <map> and <stdexcept>, you will assume that those are the only dependencies for that header. You don’t get that with an include-all-you-can header. True, the header may include only the dependencies for the exposed parts—and the associated cpp file may contain tons of other includes—but that still more informative than including <usual>.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: