Jump to content

  • Log In with Google      Sign In   
  • Create Account


Using Constants in Headers


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
20 replies to this topic

#1 KingofNoobs   Members   -  Reputation: 301

Like
0Likes
Like

Posted 22 November 2012 - 06:00 AM

Hello,

I have been running into a problem in my code lately. I have a file called "Constants.h" and one called "Constants.cpp." I define all my constants there. However, when I include Constants.h in another .h file I always get compile errors when, for example, trying to allocate arrays of a GIVEN_CONSTANT size. Is there a reason for this? I never get these errors in .cpp files. It seems like the pre-processor is not expanding my #includes into the header, or maybe there is a rule that constants can't be used in headers in c++? Can someone shed some light on this situation for me?

Thanks.

- Dave Ottley

I wonder as I wander...

http://www.davesgameoflife.com


Sponsor:

#2 SiCrane   Moderators   -  Reputation: 9574

Like
0Likes
Like

Posted 22 November 2012 - 06:02 AM

It would help to see what your code actually looks like and what compiler errors you're actually getting.

#3 KingofNoobs   Members   -  Reputation: 301

Like
0Likes
Like

Posted 22 November 2012 - 06:14 AM

I'll give you an example. This is not exact because I have already fixed the errors with a workaround. But, for example

Constants.h:
extern const int MAX_INTS;

Constants.cpp
extern const int MAX_INTS = 1000;

Foo.h
#include "Constants.h"
class Foo {
int bar[MAX_INTS];
void DoSomething();
};

Compiler Error: "Arrays must be initialized with a constant."

*NEW* Foo.h
#include "Constants.h"
const int MAX_INTS = 1000;
class Foo {
int bar[MAX_INTS];
void DoSomething();
};

Compiles fine.

Edited by KingofNoobs, 22 November 2012 - 06:15 AM.

I wonder as I wander...

http://www.davesgameoflife.com


#4 mrbastard   Members   -  Reputation: 1573

Like
5Likes
Like

Posted 22 November 2012 - 06:21 AM

As the constant is defined in a different translation unit, it's value cannot be known at compile time, only at link time.


#5 SiCrane   Moderators   -  Reputation: 9574

Like
4Likes
Like

Posted 22 November 2012 - 06:52 AM

Why aren't you putting the constants in the header?

#6 Álvaro   Crossbones+   -  Reputation: 12998

Like
5Likes
Like

Posted 22 November 2012 - 07:53 AM

Simply write this in your header file, and nothing in the .cpp file:
const int MAX_INTS = 1000;


#7 Acid-Chris   Members   -  Reputation: 491

Like
1Likes
Like

Posted 23 November 2012 - 02:55 AM

First of all, in your Constants.cpp file you need to write "const int MAX_INTS = 1000; "
The extern keyword means "declare without defining". In other words, it is a way to explicitly declare a variable, or to force a declaration without a definition.

The practice itself (putting the initialization into the cpp file) is a matter of personal taste. I personally like it because if i need to change the value for whatever reason, not every single file which includes the header file is compiled again.

But i wouldnt use "extern" anymore....i like static const uint32 MAX_INTS; in a header file more. ;-)

#8 C0lumbo   Crossbones+   -  Reputation: 2214

Like
0Likes
Like

Posted 23 November 2012 - 04:12 AM

.

Edited by C0lumbo, 23 November 2012 - 04:24 AM.


#9 e‍dd   Members   -  Reputation: 2105

Like
5Likes
Like

Posted 23 November 2012 - 10:34 PM

Simply write this in your header file, and nothing in the .cpp file:

const int MAX_INTS = 1000;


That will of course solve the problem, but for the OP's benefit I think it might be worth giving a little more background on why 'extern' isn't needed here.

For example, you can't put "int MAX_INTS = 1000;" in a header file without opening yourself up to ODR violations, despite the trivial visual differences between this definitions and "const int MAX_INTS = 1000;".

Muddying the waters further are differences in semantics between C and C++.

In C++, a definition such as "const int MAX_INTS = 1000;" defines the MAX_INT object with internal linkage. This means that if the definition resides within a header, and that header is #included in to multiple translation units, you're really creating a number of distinct objects (though I think a linker could still put them all at the same location in the end, as a space optimization).

However, "int MAX_INTS = 1000;" defines an object with external linkage because MAX_INTS is not const. This means that if this definition resides within a header, and that header is #included in to multiple translation units, you're defining a single object multiple times. This is an ODR (one definition rule) violation, which says (approximately) that objects with external linkage must have a single definition across the entire program. So in this case, the correct thing to do is indeed to declare MAX_INTS with 'extern' in a header, and then define it in a single translation unit.

In C, everything we've seen so far has external linkage. If you want to put a constant in a header, you either use a #define instead, or put 'static' in front of the definition to give each copy of the variable internal linkage within their respective translation units. You can also use 'static' in the C++ definitions too without causing harm, except for the redundant keyword.

There are some other rules too, making the whole subject quite intricate. But the important things to understand are the concepts of internal and external linkage. Knowledge about how and when definitions and declarations affect linkage will come with time and practice.

(Edited to remove incorrect stuff about floats).

Edited by e‍dd, 23 November 2012 - 10:49 PM.


#10 KingofNoobs   Members   -  Reputation: 301

Like
0Likes
Like

Posted 25 November 2012 - 01:31 AM

Thank you all for your comments. I have decided to go with the

#ifndef
#define MAX_INTS 1000
#endif

route because it doesn`t waste any memory. I can`t see a downside to it either, and this is how the Microsoft .h files are organized. Until next time...

- Dave Ottley

I wonder as I wander...

http://www.davesgameoflife.com


#11 fastcall22   Crossbones+   -  Reputation: 4271

Like
0Likes
Like

Posted 25 November 2012 - 02:10 AM

... and this is how the Microsoft .h files are organized.

*Cringe*

The windows headers aren't exactly the pinnacle of clean non-namespace-polluting headers...
c3RhdGljIGNoYXIgeW91cl9tb21bMVVMTCA8PCA2NF07CnNwcmludGYoeW91cl9tb20sICJpcyBmYXQiKTs=

#12 Álvaro   Crossbones+   -  Reputation: 12998

Like
2Likes
Like

Posted 25 November 2012 - 02:41 AM

You really got the wrong conclusion from this. You don't know if using a const variable wastes any memory, so that's a terrible reason to pick one over the other. An optimized build with g++ generates identical code for both.

A const variable behaves like any other variable, while a macro constant has surprises: you can't take its address, it doesn't have a namespace, it doesn't obey the usual scoping rules, you can't access its value from a debugger, it can't be an object of a class...

You should write your code to be as clear as possible, minimizing surprises, not whether you might save 4 bytes (which you won't anyway). And therefore you should prefer using const variables over macros to represent constants.

#13 KingofNoobs   Members   -  Reputation: 301

Like
0Likes
Like

Posted 25 November 2012 - 05:38 AM

Alvaro,

Thank you for that additional input. Could you possibly link or attach an example of a (if possible complex) header defining const variables that use the features you list above such as namespaces, being objects in classes, having their addresses taken, etc. I guess I need to see what kind of complexity doing this entails, and if I will ever use those features.

-Dave Ottley

I wonder as I wander...

http://www.davesgameoflife.com


#14 e‍dd   Members   -  Reputation: 2105

Like
5Likes
Like

Posted 25 November 2012 - 08:54 AM

Good examples of the namespacing problem come directly from Microsoft's <windows.h> header itself.

I bet many people here have hit the problem of trying to std::numeric_limits<int>::max(), or std::max(a, b) after #include-ing <windows.h>, only have the compiler to spit a weird error at them. This is because <windows.h> uses the preprocessor to define 'min' and 'max' macros.

Another example of preprocessor damage, also caused by <windows.h>:

#include <windows.h>

namespace fs
{
    bool DeleteFile(const fs::Path &amp;amp;filename)
    {
        // ...
    }
}

This won't compile without additional hacks. This is because <windows.h> uses the preprocessor to define "DeleteFile" as either "DeleteFileA" or "DeleteFileW", depending on whether or not you've defined UNICODE. I've even gone to the care of putting my DeleteFile in its own namespace. But it doesn't make a difference to the preprocessor.

Not only does the preprocessor not respect C++ namespaces, but there's no way of putting macros themselves in to any kind of namespace, meaning that everyone has to (or really should) add a prefix to the front of their macros.

So <windows.h> is a horrible header. I could list a number of other real-world examples where it has damaged perfectly reasonable code. I actually have a policy to never include it in a public header because of the carnage it can cause. I would not recommend using it for inspiration!

Though sometimes useful, the preprocessor should be avoided where possible, especially if there's an alternative, which in this case there is. Use "const int MAX_INTS = 1000;" instead. You really shouldn't be worrying about wasting 4 bytes here or there.

Edited by e‍dd, 25 November 2012 - 09:03 AM.


#15 Álvaro   Crossbones+   -  Reputation: 12998

Like
1Likes
Like

Posted 25 November 2012 - 09:58 AM

The code in the header file would look exactly as I posted above, except for possibly being in a namespace. But if you aren't using namespaces [yet], there is no point in putting this particular thing in a namespace.

I can't post any code from work, but we do this type of thing all the time there.

Just test to print the value of the constant from a debugger, and you'll immediately see one of the benefits of using a const variable.

#16 SiCrane   Moderators   -  Reputation: 9574

Like
1Likes
Like

Posted 25 November 2012 - 10:53 AM

Keep in mind that the MS windows headers are designed to be used from C as well as C++. So while there are (reasonably) good reasons for what they do in their code, you should only copy them if you are working under the same kind of constraint.

Also, for integral constants, another option is using an enum. Like a #define it never occupies storage, but like const variables it respects scope.

#17 KaiserJohan   Members   -  Reputation: 1126

Like
0Likes
Like

Posted 26 November 2012 - 08:10 AM

If I have a const string I access from several places, should I declare it extern and move definition to a .cpp or make it static? Since just making it const string in the header would create multiple objects?

#18 SiCrane   Moderators   -  Reputation: 9574

Like
0Likes
Like

Posted 26 November 2012 - 08:19 AM

That's not something you really need to worry about. Most linkers will fold identical constant data (including strings) into a single instance. Ex: MSVC's /opt:icf behavior.

#19 KingofNoobs   Members   -  Reputation: 301

Like
0Likes
Like

Posted 26 November 2012 - 10:45 AM

Thank you all for your kind responses. So, should I put a namespace i.e. Constants:: around my constants, or would that be a waste of keystrokes?

I wonder as I wander...

http://www.davesgameoflife.com


#20 SiCrane   Moderators   -  Reputation: 9574

Like
0Likes
Like

Posted 26 November 2012 - 10:54 AM

Unless your constants have some sort of logical reason that they should be either grouped together or sectioned off from other symbols, then there's no point in creating a namespace just for constants. For example, you might group constants that form flags together or constants for private use separate from other symbols. But there's no point to dumping all your constants in a namespace just to have a namespace.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS