C++ extern const

Programming, for all ages and all languages.
Post Reply
User avatar
Neo
Member
Member
Posts: 842
Joined: Wed Oct 18, 2006 9:01 am

C++ extern const

Post by Neo »

I was wondering what is the best solution to this problem.

Consider the following 2 files.

In file1.cpp

Code: Select all

extern const int a1 = 100;
extern const int a2 = 200;
.......
........
<Use the const for some checking>
.........
In file2.cpp (I need to use the same values here)

Code: Select all

extern const int a1;
extern const int a2;
..........
..........
int val = getVal();
switch(val){
  case a1:
     .....
     break;
  case a2:
     break;
  default:
     break;
}
...........
Now this does not compile (understandably).
So the best way (I thought) was to include these in a header file (say a.h) and the #include them in the 2 files (or rather introduce them in a header file that was common to both of the files a1.cpp and a2.cpp).

There is also a possibility that these 2 files could be individually compiled and only the .o's used (when a patch is required)

A senior colleague mentioned that when we individually compile these files then maybe the variables would un-necessarily occupy space in both .o's.

I was wondering if this was correct and if so then what would be the best way to use these const's?
Only Human
User avatar
gaf
Member
Member
Posts: 349
Joined: Thu Oct 21, 2004 11:00 pm
Location: Munich, Germany

Post by gaf »

So the best way (I thought) was to include these in a header file and then #include them in the 2 files.
Sounds like a good idea to me:

Code: Select all

(constant.hpp)
const int a1 = 100;
const int a2 = 200;

(code_one.cpp)
#include "constant.hpp"

switch(something)
{
   case a1:
     Panic();
   
   default:
     DontPanic():
}

(code_two.cpp)
#include "constant.hpp"

switch(something)
{
   case a1:
     DontPanic();
   
   default:
     Panic():
}

g++ -c code_one.cpp
g++ -c code_two.cpp

ld code_one.o code_two.o -o code.exe
Note that this would not work for non-constant variables as they can be seen from all source-files. The linker couldn't build the binary as there would be several definitions of the same variable.

In C++ constant variables are however by default local to help optimization. This is also the reason why your original code didn't work: The case statement requires a constant expression, but the compiler can't know which value has been assigned to the extern variable.
A senior colleague mentioned that when we individually compile these files then maybe the variables would un-necessarily occupy space in both .o's.
In your case the compiler knows the value of the variables. It will simply replace all a1 with an immediate value, so that the variable won't appear at all in the binary.

regards,
gaf
User avatar
Neo
Member
Member
Posts: 842
Joined: Wed Oct 18, 2006 9:01 am

Post by Neo »

gaf wrote: In your case the compiler knows the value of the variables. It will simply replace all a1 with an immediate value, so that the variable won't appear at all in the binary.

regards,
gaf
But it would still occupy space in the object files right?
So if we only replace object files and not binaries this would cause a waste of memory?
Only Human
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Post by Candy »

What Gaf says sounds logical and something I could expect, but I wouldn't expect it not to output it at all. If you want to explicitly tell it that you won't put it in other files (so it can certainly perform this optimization) put them in an anonymous namespace:

Code: Select all

namespace {
 const int a1 = 13590;
 const int a2 = 39057;
}
That's more or less equal to the older static before global variables as you would do in C to achieve the same. You can then put this in the header.

The compiler will see that the variables are constant (as per const) and that they're not accessed outside of this file (by the namespace) so if it's not very straightforward it'll leave them out.
User avatar
Neo
Member
Member
Posts: 842
Joined: Wed Oct 18, 2006 9:01 am

Post by Neo »

That namespace trick looked interesting.
Anyway they decided to stick to the const in the common header after all this.
Must have been scared to try this :)
Only Human
User avatar
gaf
Member
Member
Posts: 349
Joined: Thu Oct 21, 2004 11:00 pm
Location: Munich, Germany

Post by gaf »

Candy wrote:If you want to explicitly tell it that you won't put it in other files (so it can certainly perform this optimization) put them in an anonymous namespace.
A namespace isn't strictly needed for constants as they're already by default local in C++. It's even possible to use different values for the same constants in different source-files:

Code: Select all

(code_one.cpp) -> const int a = 0xABC;  // local variable
(code_two.cpp) -> const int a = 0xDEF;  // local variable
If you want a constant to be visible outside of your module it must be declared as extern. This overrides the default behaviour and allows you to refere to the variable from other modules:

Code: Select all

(code_one.cpp) -> extern const int a = 0xABC;  // global variable (definition)
(code_two.cpp) -> extern const int a;          // extern variable (declaration)
At the moment I unfortunately can't find any useful references about this topic on the internet. My Stroustrup however does include some bits of information in chapters §9.2 (linkage of constants) and §5.4 (constants and optimization). If I remember correctly you once mentioned having a copy of the book ?
Neo wrote:But it would still occupy space in the object files right? So if we only replace object files and not binaries this would cause a waste of memory?
It really depends on the constants you want to use. If their value is known to the compiler when it parses through your source there's no need to create any object. Every occurance of your constant in the source will be replaced by an immediate value and there won't be any variable in the object-files or the binary. If the value of the constant isn't know to the compiler a variable has to be created.

Code: Select all

const int c0 = 0xDEADBABE;          // Known at compilation time
const int c1 = CreateRandom(time);  // Unknown -> object needed

extern const int c2; // Unknown -> one (!) object needed
const int* p = &c0;  // Object for c0 needed
Note that arrays of constants usually have to be created in memory as it's impossible for the compiler to know in advance how the data will get accessed. If you want to use such large structures you should probably use global variables to save memory. Local constants should be used for integers (f.ex. magic values) to get the best performance and save memory.

cheers,
gaf
Post Reply