[Solved] Using constants in several cpp files
-
Hi, just a quick question... Maybe I am missing something really obvious, this seems an easy topic, but even then it puzzles me, because I just can't see where the problem is...
There are some constants I have defined in a constants.h, let me just quote two:
#ifndef CONSTANTS_H #define CONSTANTS_H const char *STR_NEWLINE = "\n"; const int BOOT_TIMEOUT = 2000; #endif // CONSTANTS_H
Now I use this constants.h file in two .cpp files via include: #include "constants.h"
Currently it's used in the mainwindow.cpp and a subdialog, the include looks exactly the same (thanks to copy'n paste ;) ) and is also in the .cpp file.Now I get 'multiple definition of ...' errors for all the char * constants but not for the int constants. Why would I not be able to include these constants twice, even I put in the "ifndef..." pattern? What's the thing I miss here...?
Thanks a lot,
Stephan -
Define once in some file and use extern keyword ?
-
Hi, sorry I never replied before, but I was busy working on other corners of the project. In the meanwhile I have introduced quite a lot of constants which I just think is useful to be grouped in a constants file. So I have this constant object with a lot of "static const ..." definitions which I would say is fine. In includes I have received from other parts of the project I find a lot of preprocessor #defines...
Now, I come from the Java corner and appreciate the limited scope of the constants object and the constants within. From the principal point of view, how would you all recommend to do it? Do you have specific reasons to do it one way or the other? What's the silver bullet - if there is one - to it?
Stephan
-
So I have this constant object with a lot of "static const ..." definitions which I would say is fine.
the problem with that is, that each file which includes the header file with the static variables gets it's own copy of the variable. Which no idead especially when using a lot of such constants and/or complex types.
So to make sure the variable is only initialized once in the whole app/lib (the same same memory for all includes) follow Dheerendra suggestion:
in the constants.h file:
extern const int MY_CONSTANT;
then in a .cpp (e.g. constants.cpp)
const int MY_CONSTANT = 0;
-
Ok, right, I missed that again... Now, the header file is - as usual - prefixed with the #ifdef, #define combination. If I remove the static, then I receive the multiple definition error. But why, would the #ifdef, #define not omit the multiple compilation of the code? Well, why does it not solved this? And if it doesn't, why the heck do we write it all the time?
Carries us to the format you have suggested before with declaration in the header and definition in the source file. But that's a lot of double typing, I would suggest a rather error prone approach. Does this suggests to resign to the use of preprocessor #defines for constants definitions?
Stephan
-
No "best practice" suggestions with regard to this topic?
-
Well, why does it not solved this?
You need to build a mental image of how compilation and linking works to answer this.
To simplify - compilers don't compile headers. They only compile .cpp files and before they do they sorta copy/paste text from header into the cpp. Then each cpp gets compiled into a separate object file. After that linker gets these object files and glues them together to form the resulting library/executable.Knowing that consider this example:
//foo.h const int variable; //bar.cpp #include "foo.h" //bazz.cpp #include "foo.h"
This pastes foo.h in both cpps. Compiler happily compiles this and creates two object files bar.obj and bazz.obj, both of which contain a variable. Now linker takes these and tries to glue them together. Ups.. it has two symbols called "variable". It throws an error.
Now surround the .h with #ifdef and... it changes nothing. It gets pasted into cpps just the same and the linker will still give an error. Bummer.
Now let's throw a static into the mix. static means a variable definition with object file scope i.e it still gets copy/pasted into the cpps and creates one symbol for each file but "scoped" to that file. Sorta like a namespace, just implicit. You can't access bar's copy of the variable from bazz and you can't access bazz's copy from bar so there's no clash. You have two objects but they don't look the same to the linker. It uses copy from bar in bar.obj code and copy from bazz in code from bazz.obj.
That being said a word of caution here. It's kinda dangerous if you mix it with #ifdefs, like so://foo.h #ifdef BLAH static const int variable = 42; #else static const int variable = 24; #endif //bar.cpp #include "foo.h" // will put variable with value 24 into the bar.obj //bazz.cpp #define BLAH #include "foo.h" // will put variable with value 42 into the bazz.obj
So you see that it can lead to situation that you seem to be using the same constant, but it gets a different value depending on which part of your code uses it. Scarry to debug ;) My personal recommendation here: don't do it.
As for the extern it does something else. an extern means more or less "Please compiler, don't create an object of this variable. I'm just hinting to you that there will be a variable like this somewhere in the program but let linker worry about it. Just pretend it's defined but don't define it yourself." Then you have this variable really typed in some cpp, this time without extern, so it gets created by the compiler. It doesn't yet know it's the same thing it just creates some variable object like it was defined only i that cpp. After that the linker steps in, sees that variable and says "oh, that's the variable you were mentioning with the extern keyword. I'll beter put it now wherever it was mentioned.". Problem solved.
It doesn't fall into that #ifdef trap because now the variable is defined in a single place and just hinted at (with extern) in multiple. If you happen to define a variable called like this in some other cpp you will get the linker's (un)friendly multiple definition error again. A little more typing but safer. My personal choice.And if it doesn’t, why the heck do we write it all the time?
The include guards (#ifdefs) are not intended to protect from multiple definition. They protect from multiple declaration. Consider this example:
//foo.h class A {}; //bar.h #include "foo.h" //bazz.h #include "foo.h" //some.cpp #include "bar.h" // declares A #include "bazz.h" // declares A... again? Um.. so which one do you mean?
This time it's the compiler that throws an error. Not the linker. The compiler has a cpp with two declarations of A so it protests.
Now let's fix this:
//foo.h #ifndef FOO #define FOO class A {}; #endif //bar.h #include "foo.h" //bazz.h #include "foo.h" //some.cpp #include "bar.h" // defines FOO, declares A #include "bazz.h" // FOO is defined so it does nothing
Now there's a single declaration of A and compiler is happy.
Hope nobody fell asleep reading this ;)
-
Hope nobody fell asleep reading this ;)
Definitely not. Even all this sounds familiar, somehow I was still having question marks in my mind. After reading this it seems to be much clearer! I may have been mixing up concepts of Java, C, C++, Delphi in my mind which all of them I have to use often in parallel - depending on the project. So this pulled me back to the red thread, thanks for that. :o)
Stephan
-
@Chris-Kawa You Kidding? Your explanation was very interesting, clear and easy to understand. Thank you very much.
I loved the below line,
"Please compiler, don't create an object of this variable. I'm just hinting to you that there will be a variable like this somewhere in the program but let linker worry about it. Just pretend it's defined but don't define it yourself."