C++ best way to define cross-file constants - c++

I am working on a game and have an interesting question. I have some game-wide constant values that I want to implement in one file. Right now I have something like this:
constants.cpp
extern const int BEGINNING_HEALTH = 10;
extern const int BEGINNING_MANA = 5;
constants.hpp
extern const int BEGINNING_HEALTH;
extern const int BEGINNING_MANA;
And then files just #include "constants.hpp"
This was working great, until I needed to use one of the constants as a template parameter, because externally-linked constants are not valid template parameters.
So my question is, what is the best way to implement these constants? I am afraid that simply putting the constants in a header file will cause them to be defined in each translation unit. And I don't want to use macros.
Thanks

Get rid of the extern and you're set.
This code works perfectly fine in a header, because everything is "truly constant" and therefore has internal linkage:
const int BEGINNING_HEALTH = 10;
const int BEGINNING_MANA = 5;
const char BEGINNING_NAME[] = "Fred";
const char *const BEGINNING_NAME2 = "Barney";
This code cannot safely be put in a header file because each line has external linkage (either explicitly or because of not being truly constant):
extern const int BEGINNING_HEALTH = 10;
extern const int BEGINNING_MANA = 5;
const char *BEGINNING_NAME = "Wilma"; // the characters are const, but the pointer isn't

How about enums?
constants.hpp
enum {
BEGINNING_HEALTH = 10,
BEGINNING_MANA = 5
}

Use "static const int" in your .hpp file, and put nothing in the .cpp file (except whatever other code you have there of course).

make use of namespaces:
namespace GameBeginning {
const int HEALTH = 10;
const int MANA = 5;
};
then u can use as player.health = GameBeginning::HEALTH;

Most compilers simply don't allocate space for const POD values. They optimize them out and treat them as if they had been #defined, don't they?

What ever happened to a simple:
#define BEGINNING_HEALTH 10
Man, those were the days.
Oh wait, those still are the days!

perhaps something along the lines of a static class?
class CONSTANTS {
public:
static inline int getMana() { return 10;};
};

As a quick answer to the title question, a singleton pattern is a possible best, C++ way to define cross-file constants and insure only one instance of the object.
As far as the template parameter problem, you need to pass a type not a value. Your type is "int".

Related

The best way to avoid using lots of global variables or make them easy to access and modify

I'm translating an old project, written before object oriented programming became something that is common and I have lots of global variables there. It's a project written in Pascal and I try to rewrite it in modern c++11 way, as well as trying to achieve the most possible portability as I can.
And my purpose is to make the project easy to modify for other people. And my problem is that I get lots of LNK2019 errors in Visual Studio right now due to these variables are not well declared.
The thing is that I have some header files that are full of global variables like
bool DS_Flag_0;
...
bool DS_Flag_11;
and
typedef unsigned short int UInt16;
//...
const UInt16 DayManth;
const UInt16 DayYear;
const UInt16 DayLongYear;
const UInt16 DayPer;
const int SecMin;
const double SecQuart;
const double SecClock;
const double SecHalfDay;
const double SecDay;
const double SecWeek;
const double SecYear;
const double SecPer;
And I need somehow to use all of these variables inside of my project in almost every file possible. (Yes, that's a really bad designed project but I do not have that much time to rebuild it from scratch so..)
There are some options that I see:
Use "extern" modifier and create lots of ".cpp" initialization files because I need to initialize those as well
Somehow make it possible (may be) to initialize all of them inside of ".h" headers. It this a good programming practice if I want to make the project readable for other people?
Maybe I somehow can implement object-oriented patterns here to avoid lots of variables and make it more modern, can I?
The purpose of the project is to translate lots of satellite messages from binary file data into another binary format and I need at first to get it to work with these data (but this information is not that neccessary to answer the question).
Update:
To be more certain, I have many variables inside of files with namespace "Prm" and "Prm_BinDS".
I have Files "Prm_IDD_VAR.h", "Prm_BinDS_MayUnit_Data.h" and "Prm_IDD_VAR.h" and they all share the "Prm" namespace. There are vars of type "bool", structs with types of "UInt16, float, double and unsigned char", single unsigned chars, floats, doubles and even std::string's and arrays of all of these types.
namespace Prm {
bool GPS_FlagL1[33];
...
bool Gali_FlagL3[51];
//...
}
All of these vars have to be initialized inside of the extern ".cpp"s and I also want to make them have initial values. Is this also possible to initialize them somehow to all zeros when they are declared? (I heard that if I use 'static' - I can't use my global variables anywhere except for the file where they are declared)
Roughly you need to do this:
variables.h
// declare all global variables
extern int global1;
extern int global2;
extern int global3;
...
variables.cpp
// define all global variables
int global1;
int global2 = 123; // initializing variable to something else than 0
int global3;
somefile.cpp
#include "variables.h"
...
printd("Global1 = %d\n", global1);
...
someotherfile.cpp
#include "variables.h"
...
global1 = foo;
...
Oh dear... Having had the joy of wrestling with a 1000LoC functions (which liberally accessed various global variables) I can sympathize.
First of all, I'd strongly reccommend you to get and read the book "Working Effectively with Legacy Code" by Michael C. Feathers. It will give a lot of good ideas how to work, well, with the code you have.
Secondly: Consider your testing procedures. I assume that the new program is expected to work like the old pascal one, so make sure you have a solid set of tests to verify that.
Finally: There are several ways to get such a global variable blob under control. The best option (in the medium and long term) would probably be to untangle that ball of yarn, pack global variables that belong together into structs or classes, and use dependency injection to get them to the objects (and functions) that need them (i.e. the constructor of such classes demands a pointer to said struct).
//FlagsStruct.h
struct FlagsStruct
{
int Flag_1;
int Flag_2;
}
//WorksWithFlags.h
class WorksWithFlags
{
public:
WorksWithFlags(FlagsStruct* flags);
//...
}
That way you need to create the FlagsStruct once (and only once), then hand it to all those that need to work with it.
And regarding the const values - that is pretty much the only way to handle consts. You could too partition those up into groups that belong together and create individiual header files (with them as static consts in them) for those, but beyond that I don't see much that you could do about them. On the postive side: consts are (being read-only) rather benign "globals".
The best practice would probably be separating declaration and definition manually. If you know how to use a few command line utilities, like sed, things will be very easy:
typedef unsigned short int UInt16;
//...
extern const UInt16 DayManth;
extern const UInt16 DayYear;
extern const UInt16 DayLongYear;
extern const UInt16 DayPer;
extern const int SecMin;
extern const double SecQuart;
extern const double SecClock;
extern const double SecHalfDay;
extern const double SecDay;
extern const double SecWeek;
extern const double SecYear;
extern const double SecPer;
The above could be done by one line of command:
sed -E 's/^((?:const )?(?:double|UInt16|int))/extern \1/1' whatever.cpp > globals.h
And rename the original file to globals.cpp
First, about your typedef: There's a standard type - both in C and C++ - for an unsigned 16-bit integer: std::uint16_t in <cstdint> (which is <stdint.h> in C). You should probably be using that.
Now, your global constants seem to be made up of two groups: Day and Sec. My guess would be that they would probably fit in existing classes, but given only what you've shown us, I wound consider having the following:
in file global_constants.h:
struct Day {
static const uint16_t Month; // fixed your typo
static const uint16_t Year;
static const uint16_t LongYear;
static const uint16_t Per; // confusing name here...
};
struct Sec { // is that "Second" perhaps?
static const int Min; // minutes, perhaps?
static const double Quart;
static const double Clock;
static const double HalfDay;
static const double Day;
static const double Week;
static const double Year;
static const double Per; // again, a confusing name
}
and define these values in a separate file, e.g.
in file global_constants.cpp:
// ...
const double Sec::Year = 1.0;
// ...
but again, judging from the names I'm guessing there's a fair chance you don't really need most/any of them at all.

Using Enum to store numerical constants

I recently came across code which was similar to the following:
#include <stdio.h>
class Example {
private:
enum {
BufSize = 4096,
MsgSize = 200 * 1024,
HeaderFieldLen = 16
};
public:
int getBufSize() {
return BufSize;
}
};
int main() {
Example ex;
printf("%d\n", ex.getBufSize());
return 0;
}
A class was essentially storing constants in an enum and using their values in its member functions.
Is this a valid use of an enum and if so, is there any reason to store constants in this way, as opposed to in a struct or as regular const class member variables?
There are several ways of naming numerical constants in order to avoid magic numbers. Using enumerators are one of them.
The advantage of this method over regular const variables is that enumerators are not variables. Therefore, they are not stored as variables during run-time, they are simply used by the compiler, at compile-time.
[from the comments]
So this usage would be in some ways similar to using preprocessor macros to define constants?
The downside of macros is (mainly) type safety. Macros have no type, so the compiler cannot check for you whether the types match where you use them. Also, while macros are used in C, they are very rarely used in C++ because we have better tools at our disposal.
In C++11, a better way to name these constants is to use constexpr members.
constexpr int BufSize = 4096;
constexpr int MsgSize = 200 * 1024;
constexpr int HeaderFieldLen = 16;
The above code replaces the following.
enum {
BufSize = 4096,
MsgSize = 200 * 1024,
HeaderFieldLen = 16
};
It's valid.
In the early days, not all compilers support static const data member very well. So you have to use this enum hack to simulate a static const integer data member.
Now, since compilers have supported static const data member very well, you don't have use this hack.
// in example.h
class Example {
static const int BufSize = 4096;
};
// in example.cpp
const int Example::BufSize; // definition

Trouble declaring an array using symbolic constant

This code will not compile:
#ifndef RemoteControl_h
#define RemoteControl_h
#include "Arduino.h"
class RemoteControl
{
public:
RemoteControl();
~RemoteControl();
static void prev_track();
static void next_track();
static void play_pause_track();
static void mute();
static void vol_up();
static void vol_down();
void respond(int code);
void add_code(int code, void (*func)());
private:
boolean active = true;
struct pair {
int _code;
void (*_func)();
};
const int max = 1000;
int database_length = 0;
pair database[max]; //This line doesn't compile unless I use a literal constant instead of "max"
};
#endif
But if I put the section below in the constructor for the class instead it works fine.
const int max = 1000;
int database_length = 0;
pair database[max];
Am I not allowed to declare an array within a class in c++ and use a virtual constant as the length? I am working in arduino if that makes a difference, but I expect that I am not understanding something with the c++ language since this is a standard .h file. Oh and the problem isn't the .cpp file because I completely removed it with the same results: compiles with literal constant length but not virtual constant length.
In C or C++,try using malloc() in stdlib.h, cstdlib for c++. Don't forget free()
const int max = 1000;
struct pair *ptr = malloc(sizeof(pair) * max); // allocated 1000 pairs
free(ptr); // when the amount of memory is not needed anymore
Let me first clear a few things up for you.
In C, a const variable is considered as const-qualified, it is not a compile-time constant value (unlike an integer literal, which is a compile time constant value). So, as per the rules for normal array size specification, you cannot even use a const variable in this case.
In C, we may have the provision to use VLA which enables us to use syntax like pair database[max] even if max is not a const variable but that is again some optional feature of the compiler (as per C11).
In C++, we can use a const variable as the size of array, as in C++, a const variable is a compile time constant.
So, to answer your question:
In C, your code will be ok if your compiler supports VLA. and even if max is not const.
In C++, there is no VLA, but it maybe supported as a gnu extension. If max is const, it will be ok.
The easiest fix is to just take the
const int max = 1000;
out of the class and put it above the class.
Even better would be to ensure that it is a compile-time constant like so:
constexpr int max = 1000;

Dealing with compilation times when editing headers

I'm working on a huge project which uses a system that, when running make, generates some header files that contain some constants that are used everywhere in the code. Due to the size of this project if one of those headers is changed (a constant is removed or another is added) almost the whole project must be recompiled (which sometimes takes several hours).
My initial solution was to write some sort of ConstantManager class that has a map with each code-value pair and a getter that returns that given the code as a string returns its value (pretty straight forward) and also change the header generator so it would define the constants as #defines that expand to an instance of the ConstantManager and a call to the getter. The problem with this is that it will not work with switch statements (well... case statements actually) because the return values are not constant expressions.
My question is: are there any alternative solutions to this problem or some trick to make mine work with switches?
You could split the huge header into smaller ones and include those. This might be a lot of initial work but is the most straight forward and probably compatible with your current solution.
Another option is to create to make your ConstManager class have constexpr members. For this you would not need much but you cannot use map.
ConstManager.h
namespace ConstManager {
namespace detail {
struct Constant {
char const * const name;
char const * const value;
};
Constant const * const constants;
unsigned int total_variables;
}
inline char const * const ConstManager::get(char const * needle) constexpr {
using namespace ConstManager::detail;
/* i was not able to quickly find a constexpr compatible binary search function*/
if(int i = 0; i < total_variables; ++i){
Constant & const c = constants[i];
if(strcmp(c.name, needle) == 0){
return c.value;
}
}
return nullptr;
}
}
ConstManager.c should be generated
ConstManager::detail::Constant ConstManager::detail::constants [] = {
{"first var", "first value"},
{"second var", "second value"}
};
unsigned int ConstManager::detail::total_variables = 2;
First solution proposed (this was before the first edit):
I think you should replace all your defines by regular external variables.
Although two caveats:
It will not work if those defines are used to concatenate strings
It might make inlining harder if not impossible for the compiler but if this matters only a profiler can tell.
constmgr.h
// make all your needed constansts externs
namespace Constants {
extern int const theFirstConstant;
extern char const * const someStringConstant;
}
And the source file is then generated at build time.
constexpr.cpp
int const Constants::theFirstConstant = 1000;
char const * const Constants::someStringConstant = "hihihoho";

Setting static const char[] to a predefined static const char[] fails

Hey guys! When I try to do the following in a header file
static const char FOOT[] = "Foot";
static const char FEET[] = FOOT;
I get a compiler error of error: initializer fails to determine size of FEET. I was wondering what the cause of this is, and if there is a way to rectify it. Thanks!
Even though why you get this error has been answered, there's more to the story. If you really need for FEET to be an array, then you can make it a reference instead of a pointer:
char const foot[] = "foot";
char const (&feet)[sizeof foot] = foot;
// reference to array (length 5) of constant char
// (read the declarations "from the inside-out")
char const* const smelly_feet = foot;
// constant pointer to const char
int main() {
cout << sizeof feet << ' ' << feet << '\n';
cout << sizeof smelly_feet << ' ' << smelly_feet << '\n';
cout << sizeof(void*) << " - compare to the above size\n";
return 0;
}
(More examples of the inside-out rule.)
Secondly, static at file and namespace scope means internal linkage; so when you use it in a header, you'll get duplicate objects in every TU using that header. There are cases when you want this, but I see no reason for it in your code, and it's a common error.
Regarding array size: The sizeof operator returns the memory-size of an object or instance of a type. For arrays this means the total memory-size of all of its items. Since C++ guarantees that sizeof(char) is 1, the memory-size of a char array is the same as its length. For other array types, you can divide by the memory-size of one item to get the length of an array:
void f() {
int array[5];
assert((sizeof array / sizeof *array) == 5);
}
And you can generalize it to a function template:
template<class T, int N>
int len(T (&)[N]) {
return N;
}
// use std::size_t instead of int if you prefer
This exists in boost as boost::size.
You may see code that uses sizeof array / sizeof *array, either through a macro or directly, either because it's old or doesn't want to complicate matters.
The cause is that you are saying that FEET[] is an array of chars but you're initializing it to a ptr;
To rectify, you can change FEET from FEET[] to *FEET
Could also do the same with FOOT
There is no '=' operator for arrays in C++, you need to use strcpy or something similar (which you can't do with a static const). Depending on your requirements the following might be useful:
#define contentsOfFoot "FOOT"
static const char FOOT[5] = contentsOfFoot;
static const char FEET[5] = contentsOfFoot;
static const char *feeet = FOOT;
static is deprecated, and const variables at namespace scope are effectively static, anyway. Also, please reserve ALL_CAPS for preprocessor identifiers.
A bigger question is why you are using raw arrays at all. You very likely want something more like this:
std::string const foot = "foot";
std::string const feet = foot;
Or, more likely:
#include <string>
namespace spbots {
typedef std::string string_t;
string_t const foot = "foot";
string_t const feet = foot;
}
One way would be this
static const char FOOT[] = "Foot";
static const char *FEET = FOOT;
I want to emphasise that you shouldn't put that stuff in a header file. Variables should only be put in header files if you want to make them globally visible (generally not a great idea but it's a legitimate technique).
feet.h
extern char feet[];
feet.cpp
char feet[] = "FEET";
Now when you include feet.h in a different .cpp file, say leg.cpp, the declaration in feet.h means leg.cpp can see and use the array defined in feet.cpp
The extern keyword has the opposite meaning to the static keyword you use. A typical use of static is as follows;
feet.cpp
static char feet[] = "FEET";
Now feet[] is explicitly not visible outside feet.cpp. The static keyword is responsible for this lack of visibility. So static=local, extern=global and (perhaps unfortunately?) extern is the default, so if you don't use either keyword you get extern whether you like it or not.
Because static=local, there's nothing to communicate to other modules, so no need to have a declaration in a header file for other modules to see. Everything is kept within feet.cpp, probably a better idea than a global variable.
Another point I am implicitly trying to get through with this explanation is that declarations go into header files, definitions go into .cpp files. In your question you have a definition in a header file. Not illegal, but if you include it in more than one file (and if you don't, why would you put it in a header?), you will get a single variable defined multiple times, which will give you a link error.
All of this is actually old school C rather than C++ in the modern sense of the word. In other words you are really writing C, but since C is (almost) a subset of C++, you can still use C++ to do this stuff.