4
votes

i have a problem with global variables in a c++ shared library project. my library has to be working as a standard g++ shared library (.so) as well as a dll. i did this by creating files libiup_dll.cpp and libiup_dll.h, where i have something like

#ifdef BUILD_DLL

// code for the dll: wrapper functions around the classes in my shared library

#endif

in my dll, i need functions setloglevel(int) and geterrormsg(). in all my classes, i'd then append to a global variable errormsg all error messages. this variable should then be returned by the geterrormsg() function. i implemented this by using

std::string errormsg;
int loglevel;

in libiup_dll.h (outside and #ifdefs, so it should be globally available), and then putting

extern std::string errormsg;
extern int loglevel;

in my classes' .h files (outside the class, at the top of the files)

now i have two problems:

1) when compiling a command line program with g++, which uses my library, i get errors

Building target: libiup_test Invoking: GCC C++ Linker g++ -L"/home/hilboll/src/libiup/Release" -L/usr/local/lib -o"libiup_test" ./src/stratcalc/SimpleStratosphericColumnCalculatorTest.o ./src/interp/SimpleInterpolatorTest.o ./src/Test.o -lgsl -lhdf5 -lhdf5_cpp -lblas -liup /home/hilboll/src/libiup/Release/libiup.so: undefined reference to loglevel' /home/hilboll/src/libiup/Release/libiup.so: undefined reference toerrormsg' collect2: ld returned 1 exit status make: *** [libiup_test] Error 1

even though in my command line program, there's no reference whatsoever to errormsg or loglevel.

2) when trying to compile the dll under windows with VS2008, i get

z:\src\vs\libiup_dll\libiup_dll.h(229) : error C2086: 'std::string errormsg': Neudefinition z:\src\libiup\src\stratcalc../interp/SimpleInterpolator.h(16): Siehe Deklaration von 'errormsg' z:\src\vs\libiup_dll\libiup_dll.h(234) : error C2086: 'int loglevel': Neudefinition z:\src\libiup\src\stratcalc../interp/SimpleInterpolator.h(17): Siehe Deklaration von 'loglevel'

as far as i understand, it means that VS thinks i'm defining the two variables twice. however, in SimpleInterpolator.h 16/17, there's the extern declarations only ...

it seems i somehow haven't understood how global variables work, yet. any help is greatly appreciated!

4
using a compiler which speaks English could be a good start.shoosh
showing the actual source rather than describing it would be good.Martin York

4 Answers

4
votes

The trick is to know that each .cpp file is a compilation unit - ie the things that get compiled. Each one is a total individual and know nothing about each other. Its only at the link stage that these are brought together.

Now, extern says "this variable can be found elsewhere" to the compiled cpp file, the compiler just places a reference hint to the linker to sort things out.

So, as you've put the variable definitions in a header file, chances are you're including that header into 2 (or more) cpp files. So each of those cpp files, when compiled, think they have the real variable. The linker then comes along and sees too many.

Place the variables into a cpp file of their own (or into the main cpp file), and only put extern references in header files. You should be ok with multiply-defined symbols then.

4
votes

Globals can be declared many times but must defined only once.

The "extern" keyword marks what would otherwise be a definition as a declaration.

The general idiom for how you do this is as follows:

// In a header file (declaration)
extern int myGlobal;

// In a source file (definition)
int myGlobal;

Then, where you want to reference the global, you should either repeat the extern declaration, or include the header file that already has the extern declaration.

What you should absolutely not do is put the "int myGlobal" definition (without the 'extern') into a header. If you do this, then every file that includes that header will try to define its own myGlobal, and you will end up with symbol conflicts when you link. The definition should be in one and only one source file. The extern declaration can appear in as many files or headers as you like.

In particular, what is happening with your two errors is this:

  • When you compile under Linux, the DLL header that defines the globals is eliminated by your preprocessor macros, so your globals have a declaration but no definition, and the linker complains.
  • When you compile under Windows, the DLL header is included in more than one compilation unit (source file), and so the globals have more than one definition, and the linker complains.
1
votes

You say you have:

std::string errormsg;
int loglevel;

in libiup_dll.h outside any #ifdefs. This is a problem if libiup_dll.h is included multiple times (directly or indirectly), and is likely the cause of your problem #2.

I think this might also be the cause of problem #1 if the header is included - you have the definitions of the variables brought in by the header even though you might not use them elsewhere.

The definition of these 2 variables would generally need to be in a .c or .cpp file, not in a header. Having an extern declaration for these is OK in a header.

1
votes

You said,

I implemented this by using

    std::string errormsg;
    int loglevel;

in libiup_dll.h (outside and #ifdefs, so it should be globally available),
and then putting

    extern std::string errormsg;
    extern int loglevel;

in my classes' .h files (outside the class, at the top of the files)

Instead I think you should declare the variables using extern in any number of header files, and then define the variables without extern in one-and-only-one C or CPP file.