Confused on why even though I have this header linked several times:

#ifndef ASCII_CHAR_NAMES_H
#define ASCII_CHAR_NAMES_H
extern const unsigned char ASCII_WHITE_SMILEY = 1; //☺
extern const unsigned char ASCII_BLACK_SMILEY = 2; //☻
extern const unsigned char ASCII_BLACK_HEART = 3;
extern const unsigned char ASCII_BLACK_DIAMOND = 4;
extern const unsigned char ASCII_BLACK_CLOVE = 5;
extern const unsigned char ASCII_MALE = 11;
extern const unsigned char ASCII_FEMALE = 12;
extern const unsigned char ASCII_FUNCTION = 159; //ƒ
extern const unsigned char ASCII_INFINITY = 236; //∞
#endif/* ASCII_CHAR_NAMES_H */

but this one fails when it's only linked one time for repeated definition of CONSTANT_INFINITYd and CONSTANT_INFINITYf

#ifndef NUMERIC_CONSTANTS_H
#define NUMERIC_CONSTANTS_H
extern const double CONSTANT_INFINITYd = (double)0x7ff0000000000000;
extern const double CONSTANT_NEGATIVE_INFINITYd = (double)0xfff000000000000;
extern const double CONSTANT_PId =  3.14159265358979323846264338327950288419716939937510;
extern const double CONSTANT_TWO_PId = 2*CONSTANT_PId;
extern const double CONSTANT_PI_DIV_2d = CONSTANT_PId/2;
extern const double CONSTANT_PI_DIV_4d = CONSTANT_PId/4;
extern const double CONSTANT_PI_DIV_6d = CONSTANT_PId/6;
extern const double CONSTANT_ed = 2.71828182845904523536028747135266249775724709369995;
extern const float CONSTANT_INFINITYf = (float)CONSTANT_INFINITYd;
extern const float CONSTANT_NEGATIVE_INFINITYf = (float)CONSTANT_NEGATIVE_INFINITYd;
extern const float CONSTANT_PIf = (float)CONSTANT_PId;
extern const float CONSTANT_TWO_PIf = (float)CONSTANT_TWO_PId;
extern const float CONSTANT_PI_DIV_2f = (float)CONSTANT_PI_DIV_2d;
extern const float CONSTANT_PI_DIV_4f = (float)CONSTANT_PI_DIV_4d;
extern const float CONSTANT_PI_DIV_6f = (float)CONSTANT_PI_DIV_6d;
extern const float CONSTANT_ef = (float)CONSTANT_ed;
#endif /*NUMERIC_CONSTANTS_H*/

Can you send the command lines and the outputs with error?
Also given that there is nothing "not-extern" in the headers you posted, the problem (repeated definition of...) can't be due to these as extern doesn't define anything it only declares.
Hope you do know the use of extern.

--edit--
Guess at the cause would be:
- There are multiple object files (.o file) that export this symbol.
- This in turn would be usually because you have #included the header that actually defines this symbol in two diff .cxx files
- compiled them to create two diff .o files and then
- trying to link them (both .o) together.

If there are too many source files I recommend use of nm -C -g on all .o files.

Edited 5 Years Ago by thekashyap: n/a

Thanks for the link. Helped me a lot.

//HEADER
extern const T name;
//CPP
const T name = value;

This compiles and loads fine there isn't any downside through this method?

Edited 5 Years Ago by ChaseRLewis: n/a

Maybe you should use something other than an empty string in your #define statements.

#define NUMERIC_CONSTANTS_H 1

The downside of using extern constants is that they will require linking. Basically, the header tells the code that you are compiling that when you link your application, there will be definitions for those constants available to the linker to find. So the compiler annotates those constants as having to be looked up at link-time (with associated external symbol). Also, when compiling the source file in which the constants are defined, it creates a published symbol for each constant.

The disadvantage is obviously that you get "larger" static libraries (or shared libraries) because they need to have more external symbols. This means "longer" times for linking the compiled code. Also, if compiled into shared libraries (.so or .dll), it will result in "longer" time for loading up the program. But, of course, if the number of constants is small, it really won't be that much longer (probably not noticeable).

The advantage is that you can change the value of those constants in the source file without having to recompile all the code that uses these constants (you only need to re-link them, if compiled in static libraries, and you don't need to do anything more if compiled in shared libraries).

Generally, I would recommend that you use extern constants only if you plan on changing their values fairly often. For constants that are really "constants" like PI/2, there really is no point, just take out the "extern" keyword and define them in the header (as in your original code, but without "extern").

A few other remarks:
- There is no point in giving a value of PI or e with a gazillion decimal values, even a double can typically only represent about 15 significant digits (and long double typically only 20 significant digits).

- The constants that you use for INFINITY and such are not portable. C++ includes a header #include <limits> that includes a std::numeric_limits class template that defines all the relevant constants that are supported by the platform. So, these definitions are more portable:

const double CONSTANT_INFINITYd = std::numeric_limits<double>::infinity();
const float CONSTANT_INFINITYf = std::numeric_limits<float>::infinity();

And for non-standard platforms that don't have infinity defined, you might define your own:

const double CONSTANT_INFINITYd = (std::numeric_limits<double>::has_infinity ? std::numeric_limits<double>::infinity() : (double)0x7ff0000000000000);
const float CONSTANT_INFINITYf = (std::numeric_limits<double>::has_infinity ? std::numeric_limits<double>::infinity() : (double)0x7ff00000);

But again, I don't think that your constants for the double and float infinity are really portable in any way.

- Finally, I can't say I approve of the use of upper-case names for constants. Most programmers would expect such names to correspond to a #define, and might use it as such and get unexpected (possibly broken) results. I would recommend that you use lower-case names and collect all your constants in a namespace to avoid any name-clashing:

#ifndef NUMERIC_CONSTANTS_H
#define NUMERIC_CONSTANTS_H
namespace my_constants {
  const double pi_d =  3.14159265358979323846d;
  const float pi_f = 3.14159265358979323846f;
//..
};
#endif

//use-case:
#include <iostream>
#include "numeric_constants.h"
#include <cmath>

int main() {
  std::cout << "cos(PI/2) = " << cos(0.5 * my_constants::pi_d) << std::endl;
  return 0;
};
Comments
Good desc of the limits..
This article has been dead for over six months. Start a new discussion instead.