views:

287

answers:

9

if I define my constant varibles in my header like this...

extern const double PI = 3.1415926535;
extern const double PI_under_180 = 180.0f / PI;
extern const double PI_over_180 = PI/180.0f;

I get the following error

1>MyDirectX.obj : error LNK2005: "double const PI" (?PI@@3NB) already defined in main.obj
1>MyDirectX.obj : error LNK2005: "double const PI_under_180" (?PI_under_180@@3NB) already defined in main.obj
1>MyDirectX.obj : error LNK2005: "double const PI_over_180" (?PI_over_180@@3NB) already defined in main.obj
1>MyGame.obj : error LNK2005: "double const PI" (?PI@@3NB) already defined in main.obj
1>MyGame.obj : error LNK2005: "double const PI_under_180" (?PI_under_180@@3NB) already defined in main.obj
1>MyGame.obj : error LNK2005: "double const PI_over_180" (?PI_over_180@@3NB) already defined in main.obj

but If I remove those constants from the header and put them in the document that is including the header like this...

const double PI = 3.1415926535;
const double PI_under_180 = 180.0f / PI;
const double PI_over_180 = PI/180.0f;

It works

Does anyone have Idea what I might be doing wrong ??

Thanks

+4  A: 

The extern storage class for them is almost certainly the cause of the problem you're seeing. If you remove it, the code will probably be fine (at least in this respect).

Edit: I just noticed that you've tagged this as both C and C++. In this respect C and C++ are really quite different (but from the error messages, you're apparently compiling as C++, not C). In C++, you want to remove the extern, because (by default) const variables have the static storage class. That means each source file (translation unit) will get its own "copy" of the variable, and there won't be any conflict between definitions in different files. Since you're (probably) only using the values, not treating them as variables, having multiple "copies" won't hurt anything -- none of them will be allocated storage space.

In C, extern is rather different, and removing the extern won't make any real difference, because they'll be extern by default. In this case, you really need to initialize the variables in exactly one place, and declare them extern in the header. Alternatively, you can add the static storage class that C++ will add by default when/if you remove the extern from the header.

Jerry Coffin
+1  A: 

You need to declare the contants in the header and then define them in one of your code files. If you do not declare them anywhere, then there is a linker error when it tries to tie the declaration to the actual definition. You can also get away with using #ifdef statements to have one definition within the header.

Make sure they are declared in a header that is included by everyone that needs them and make sure they are defined exactly once.

Jacob

TheJacobTaylor
+3  A: 

extern means the 'real' definition of the variable is elsewhere, and the compiler should trust that things will hook up at link time. Having the definition inline with the extern is weird and is what's munging up your program. If you want to have them be extern, just define them exactly once elsewhere in your program.

Carl Norum
+1  A: 

If you want to define constants in header files, use static const. If you use extern, the linker is right to complain about multiple definitions because each including source file will supply memory for the variable if you assign a value.

Christoph
+1  A: 

It looks like that header file is getting included multiple times. You need to add guards.

At the top of each header file you should have something like:

#ifndef MY_HEADER_FILE_NAME_H
#define MY_HEADER_FILE_NAME_H

...

// at end of file
#endif

If you are using g++ or MSVC then you can just add:

#pragma once

At the top of each header file, but that isn't 100% portable.

Also, you shouldn't define constants in header files, only declare them:

// In header file
extern const int my_const;


// In one source file
const int my_const = 123;
Peter Alexander
Include guards only protect against multiple inclusion in the same file. Lack of them will produce a problem at compile time. He's running into a problem at link time, because of attempting to define the same symbols once each in multiple files.
Jerry Coffin
I addressed that in my answer.
Peter Alexander
A: 

in declaring global const within header causes that each compilation unit including this hader will have own definitions global definitions with the same name. Then linker does not like that.

If You really need these in header then probably You should declare them as static.

lollinus
+11  A: 

You define objects with external linkage in header file. Expectedly, once you include that header file into multiple translation units, you'll get multiple definitions of the same object with external linkage, which is an error.

The proper way to do it depends on your intent.

(1) You can put your definitions into the header file, but make sure that they have internal linkage.

In C that would require an explicit static

static const double PI = 3.1415926535; 
static const double PI_under_180 = 180.0f / PI; 
static const double PI_over_180 = PI/180.0f; 

In C++ static is optional (because in C++ constant objects have internal linkage by default)

const double PI = 3.1415926535; 
const double PI_under_180 = 180.0f / PI; 
const double PI_over_180 = PI/180.0f; 

(2) Or you can put mere non-defining declarations into the header file and put the definitions into one (and only one) implementation file

The declarations in the header file must include an explicit extern and no initializer

extern const double PI; 
extern const double PI_under_180; 
extern const double PI_over_180; 

and definitions in one implementation file should look as follows

const double PI = 3.1415926535; 
const double PI_under_180 = 180.0f / PI; 
const double PI_over_180 = PI/180.0f; 

(explicit extern in the definitions is optional, if the above declarations precede the definitions).

Which method you will choose depends on your intent.

The first method makes it easier for the compiler to optimize the code, since it can see the actual value of the constant in each translation unit. But at the same time conceptually you get separate, independent constant objects in every translation unit. For example, &PI will evaluate to a different address in each translation unit.

The second method creates truly global constants, i.e. constant objects that are shared by the entire program. For example, &PI will evaluate to the same address in each translation unit. But in this case the compiler can only see the actual values in one and only one translation unit, which might impede optimizations.

AndreyT
+1. Just wanted to mention that in C a `static const` variable is not a constant expression like you'd need for the size of an array for example. I guess that's why `#define`s are more popular in C.
sellibitze
+1 for a good description and teaching me something I didn't know: "in C++ constant objects have internal linkage by default"
Michael Burr
@sellibitze: Yes, but this mostly becomes an issue with integral constants. The impact on non-integral constant objects is negligible, if at all existent.
AndreyT
+1 great description of what's happening and solutions.
Mark B
A: 

The problem is that you are initializing the variables in the header file; this creates a defining declaration, which is repeated in every file that includes that header,hence the multiple definition error.

You want a non-defining declaration (no initializer) in the header file, and put the defining declaration in one of the implementation files.

John Bode
A: 

A lot of incorrect responses below. The ones that are correct are the ones telling you to remove the extern as sellibitze has also said in his comment are correct.

Because these are declared const, there is no problem having the definition in the header. C++ will inline a const for a built in type unless you attempt to take its address (a pointer to a const) in which case it will instantiate it with static linkage, you may then also get multiple instantiations in separate modules, but unless you expect all pointers to the same const to have the same address, this is not a problem.

Clifford