Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to make a global "constant" in C++

Tags:

c++

variables

Typically, the way I'd define a true global constant (lets say, pi) would be to place an extern const in a header file, and define the constant in a .cpp file:

constants.h:

extern const pi;

constants.cpp:

#include "constants.h"
#include <cmath>
const pi=std::acos(-1.0);

This works great for true constants such as pi. However, I am looking for a best practice when it comes to defining a "constant" in that it will remain constant from program run to program run, but may change, depending on an input file. An example of this would be the gravitational constant, which is dependent on the units used. g is defined in the input file, and I would like it to be a global value that any object can use. I've always heard it is bad practice to have non-constant globals, so currently I have g stored in a system object, which is then passed on to all of the objects it generates. However this seems a bit clunky and hard to maintain as the number of objects grow.

Thoughts?

like image 506
MarkD Avatar asked Jul 06 '10 19:07

MarkD


2 Answers

It all depends on your application size. If you are truly absolutely sure that a particular constant will have a single value shared by all threads and branches in your code for a single run, and that is unlikely to change in the future, then a global variable matches the intended semantics most closely, so it's best to just use that. It's also something that's trivial to refactor later on if needed, especially if you use distinctive prefixes for globals (such as g_) so that they never clash with locals - which is a good idea in general.

In general, I prefer to stick to YAGNI, and don't try to blindly placate various coding style guides. Instead, I first look if their rationale applies to a particular case (if a coding style guide doesn't have a rationale, it is a bad one), and if it clearly doesn't, then there is no reason to apply that guide to that case.

like image 159
Pavel Minaev Avatar answered Oct 20 '22 14:10

Pavel Minaev


I can understand the predicament you're in, but I am afraid that you are unfortunately not doing this right.

The units should not affect the program, if you try to handle multiple different units in the heart of your program, you're going to get hurt badly.

Conceptually, you should do something like this:

       Parse Input
            |
 Convert into SI metric
            |
       Run Program
            |
Convert into original metric
            |
      Produce Output

This ensure that your program is nicely isolated from the various metrics that exist. Thus if one day you somehow add support to the French metric system of the 16th century, you'll just add to configure the Convert steps (Adapters) correctly, and perhaps a bit of the input/output (to recognize them and print them correctly), but the heart of the program, ie the computation unit, would remain unaffected by the new functionality.

Now, if you are to use a constant that is not so constant (for example the acceleration of gravity on earth which depends on the latitude, longitude and altitude), then you can simply pass it as arguments, grouped with the other constants.

class Constants
{
public:
  Constants(double g, ....);

  double g() const;

  /// ...
private:
  double mG;

  /// ...
};

This could be made a Singleton, but that goes against the (controversed) Dependency Injection idiom. Personally I stray away from Singleton as much as I can, I usually use some Context class that I pass in each method, makes it much easier to test the methods independently from one another.

like image 41
Matthieu M. Avatar answered Oct 20 '22 13:10

Matthieu M.