3

My mostly header based library makes use of a global variable placed in the library's namespace (BigNum). The definition of the variable is as follows:

namespace BigNum{
/**
* __Some lower powers of ten prestored for fast runtime lookup.__*/
const uintmax_t pten[20]={
1LU, 10LU, 100LU, 1000LU, 10000LU, 100000LU, ....
};
}

Everything works fine as long as I have this definition in my main.cpp file (really the only nonheader file my testing config has) and my headers have extern uintmax_t pten[]; in them (provided they also include cstdint, where uintmax_t is typedefined).

I would, however, like to have this definition elsewhere, so I created a global.cpp file with the above as its contents and made sure my Makefile linked it to the rest of the files. Interestingly enough, compiling with the definition of pten in both main.cpp and global.cpp works fine (I would have expected double definition errors), but removing the definition from main.cpp while leaving it in global.cpp leads to linking errors.

I checked the generated global.o and it does contain the pten symbol (its mangled form). Attempting to link main.o with global.o manually in either order fails.

Any ideas?

Additional info: Here's an elementary example demostrating the problem.

main.cpp

    #include <cstdint>
    #include <iostream>

    namespace BigNum{
    extern const uintmax_t pten[];
    }
    int main( int argc, const char *argv[] )
    {
        using namespace std;

        cout<<BigNum::pten[0]<<endl;
        return 0;
    }

global.cpp

    #include <cstdint>

    namespace BigNum{
    /**
     * __Some lower powers of ten prestored for fast runtime lookup.__
     */
    const uintmax_t pten[20]={
        1LU, 10LU, 100LU, 1000LU, 10000LU, 100000LU, 1000000LU, 10000000LU, 100000000LU, 1000000000LU, 10000000000LU, 100000000000LU, 1000000000000LU, 10000000000000LU, 100000000000000LU, 1000000000000000LU, 10000000000000000LU, 100000000000000000LU, 1000000000000000000LU, 10000000000000000000LU
    };
    }

Compilation:

  g++ -std=c++0x -c global.cpp -o global.o
  g++ -std=c++0x -c main.cpp -o main.o
  g++ -std=c++0x global.o main.o

 >main.o: In function `main':
 >main.cpp:(.text+0x12): undefined reference to `BigNum::pten'
 >collect2: ld returned 1 exit status
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • 2
    *Linking* errors? You aren't telling us much, but if what you are telling us is accurate, I can't reproduce this error nor understand it. If you give us a minimal complete example, we might be able to clear it up. – Beta Jun 23 '13 at 14:03
  • My powers of telepathic debugging inform me that you don't have the header declaration of the extern within the BigNum namespace?? – Anya Shenanigans Jun 23 '13 at 14:09
  • @Beta Example added. Thanks for looking into it. – Petr Skocik Jun 23 '13 at 14:24

2 Answers2

2

Your code in global.cpp should be:

#include <cstdint>

namespace BigNum{
/**
 * __Some lower powers of ten prestored for fast runtime lookup.__
 */
extern const uintmax_t pten[]; //This should go in a common header file
const uintmax_t pten[20]={
    1LU, 10LU, 100LU, 1000LU, 10000LU, 100000LU, 1000000LU, 10000000LU, 100000000LU, 1000000000LU, 10000000000LU, 100000000000LU, 1000000000000LU, 10000000000000LU, 100000000000000LU, 1000000000000000LU, 10000000000000000LU, 100000000000000000LU, 1000000000000000000LU, 10000000000000000000LU
};
}

Check this guide: Normally, you would put extern const uintmax_t pten[]; in a separate common header file.

Community
  • 1
  • 1
Antonio
  • 19,451
  • 13
  • 99
  • 197
  • Thanks. It works either way: with an extern in the definition and an extern declaration in main.cpp or with a non-extern definition and an extern definition in both files. I did have a shared header with an extern declaration of pten, but I was not including it in global.cpp. If I had, that would have solved the problem. ;-) – Petr Skocik Jun 23 '13 at 18:03
-1

Problem solved. Turns out all global variables are, unlike functions, invisible from other compilation units. To make them visible, the extern keyword must be used in their definition (in global.cpp) as well.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • 1
    This is *impossible*. Do not confuse declaration with definition. When you use `extern` is always a declaration. Where do you *define* your variable? Please read [this](http://en.wikipedia.org/wiki/External_variable). – Antonio Jun 23 '13 at 16:06
  • "all global variable are, unlike functions, static and thereby invisible from other compilation units" -- that statement is *false*. – Employed Russian Jun 23 '13 at 16:34
  • @ThorX89 The code adding just `extern` works a little bit by chance, basically because you are initializing/allocating memory for the variable. – Antonio Jun 23 '13 at 17:00
  • @Employed Russian You're right. I fixed it. Not static. Defining a global variable that has the same name in multiple compilation units does generate a multiple definition error, so technically they're not static, but they do appear invisible from other comp. units, unless defined with extern. – Petr Skocik Jun 23 '13 at 17:47
  • 1
    @Antonio Except that it's *NOT impossible* if it works and moreover is defined by the standard. Check page 40 where you'll find an `extern` **definition**. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf – Petr Skocik Jun 23 '13 at 18:09