27

I have a whole bunch of constants that I want access to in different parts of my code, but that I want to have easy access to as a whole:

static const bool doX = true;
static const bool doY = false;
static const int maxNumX = 5;

etc.

So I created a file called "constants.h" and stuck them all in there and #included it in any file that needs to know a constant.

Problem is, this is terrible for compile times, since every time I change a constant, all files that constants.h reference have to be rebuilt. (Also, as I understand it, since they're static, I'm generating a copy of doX/doY/maxNumX in code every time I include constants.h in a new .cpp, leading to kilobytes of wasted space in the compiled EXE -- is there any way to see this?).

So, I want a solution. One that isn't "declare constants only in the files that use them", if possible.

Any suggestions?

Bart
  • 19,692
  • 7
  • 68
  • 77
Ben Walker
  • 908
  • 2
  • 8
  • 16
  • Why do you assume there is a physical copy? Unless there is a need for a physical location (like taking the address of the variable) then the compiler can quite happily optimize that away. – Martin York Mar 10 '12 at 19:47
  • THe compiler needs to know the value of each constant during compile time, if you want to get them folded – Gunther Piez Mar 10 '12 at 20:02
  • Can you imagine being a Microsoft programmer and having to spend the day recompiling because something changed in `Windows.h`? – Emile Cormier Mar 10 '12 at 20:40

6 Answers6

9

You declare them as extern in the header and define them in an implementation file.

That way, when you want to change their value, you modify the implementation file and no full re-compilation is necessary.

The problem in your variant isn't compilation-related, but logic related. They will not be globals since each translation unit will have its own copy of the variable.

EDIT:

The C++-ish way of doing it would actually wrapping them in a class:

//constants.h
class Constants
{
public:
   static const bool doX;
   static const bool doY;
   static const int maxNumX;
}

//constants.cpp
const bool Constants::doX = true;
const bool Constants::doY = false;
const int Constants::maxNumX = 5;
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
9

The only alternative is to make your constants extern and define them in another .cpp file, but you'll lose potential for optimization, because the compiler won't know what value they have when compiling each .cpp`.

By the way, don't worry about the size increase: for integral types your constants are likely to be inlined directly in the generated machine code.

Finally, that static is not necessary, since by default const global variables are static in C++.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • 1
    Link-time optimization will handily take care of this. – Puppy Mar 10 '12 at 19:26
  • An extern `const` variable of an integral type is no longer a compile-time expression, though, and so it can no longer be used for array bounds, in `case` statements etc. – Pavel Minaev Apr 10 '12 at 23:03
9

I think your base assumption is off.

Your other headers are usually organized by keeping together what works together. For example, a class and its related methods or two classes heavily interlinked.

Why group all constants in a single header ? It does not make sense. It's about as bad an idea as a "global.h" header to include every single dependency easily.

In general, the constants are used in a particular context. For example, an enum used as a flag for a particular function:

class File {
public:
  enum class Mode {
    Read,
    Write,
    Append
  };

  File(std::string const& filename, Mode mode);

  // ...
};

In this case, it is only natural that those constants live in the same header that the class they are bound to (and even within the class).

The other category of constants are those that just permeate the whole application. For example:

enum class Direction {
  Up,
  Down,
  Right,
  Left,
  Forward,
  Backward
};

... in a game where you want to express objects' move regarding the direction they are facing.

In this case, creating one header file for this specific set of constants is fine.

And if you really are worried about grouping those files together:

constants/
  Direction.hpp
  Sandwich.hpp
  State.hpp

And you will neatly sidestep the issue of recompiling the whole application when you add a constant... though if you need to, do it, you're paying the cost only once, better than a wrong-sided design you'll have to live off with for the rest of your work.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 1
    +1 When adding/changing a constant leads to almost all other source files needing to be recompiled, I call that phenomenon "recompiling the world". Someone else has probably already invented that expression. – Emile Cormier Mar 10 '12 at 20:32
3

What is the problem with this usage?
Do not declare a static type in header file, It does not do what you think it does.

When you declare a static in header file a copy of that variable gets created in each Translation Unit(TU) where you include that header file, SO each TU sees a different variable, this is opposite to your expectation of having a global.

Suggested Solution:
You should declare them as extern in a header file and define them in exactly one cpp file while include the header with extern in every cpp file where you want to access them.

Good Read:
How should i use extern?

Community
  • 1
  • 1
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • 2
    In this situation (because they are const) I would actually use static (by that I mean I would not declare them anything just not make them extern (thus default to static)). As it provides a better opportunity for a compiler to completely optimize away the value. – Martin York Mar 10 '12 at 19:49
  • `const` at global and namespace-scope level implies internal linkage, i.e. `static const` is the same as `const`, but not `extern const`. This behaviour was specifically implemented in C++ to avoid using macros for constants. The compiler won't put that constant into .data section of the object file if the address of that constant is not taken. It's value is going to be hardcoded, as it is essentially a compile time constant. – Maxim Egorushkin Sep 24 '14 at 21:30
3

Another approach which is best for compile times (but has some minor run-time cost) is to make the constants accessible via static methods in a class.

//constants.h
class Constants
{
public:
  static bool doX();
  static bool doY();
  static int maxNumX();
};

//constants.cpp
bool Constants::doX() { return true; }
bool Constants::doY() { return false; }
int Constants::maxNumX() { return 42; }

The advantage of this approach is that you only recompile everything if you add/remove/change the declaration of a method in the header, while changing the value returned by any method requires only compiling constants.cpp (and linking, of course).

As with most things, this may or may not be the best is your particular case, but it is another option to consider.

aldo
  • 2,927
  • 21
  • 36
0

The straight forward way is, to create non const symbols:

const bool doX = true;
const bool doY = false;
const int maxNumX = 5;

These values will be replaced by the compiler with the given values. Thats the most efficient way. This also of course leads to recompilation as soon as you modify or add values. But in most cases this should not raise practical problems.

Of course there are different solutions:

  • Using static consts, (or static const class members) the values can be modified without recompilation of all refered files - but thereby the values are held in a const data segment that will be called during runtime rather than being resolved at compile tine. If runtime perfomance is no issue (as it is for 90% of most typical code) thats OK.

  • The straight C++ way is using class enums rather than global const identifiers (as noted my Mathieu). This is more typesafe and besides this it works much as const: The symbols will be resolved at compile time.

RED SOFT ADAIR
  • 12,032
  • 10
  • 54
  • 92