tags:

views:

197

answers:

5

I'd love help diagnosing the source of a duplicate symbol error that I'm receiving when I try to compile with g++ 4.2.1.

The specific error is

ld: duplicate symbol _SOCIODEM_FILENAMES in /var/folders/c+/c+eq1Qz1Feye7vxs5mQOUE+++TI/-Tmp-//ccP3yVgF.o and /var/folders/c+/c+eq1Qz1Feye7vxs5mQOUE+++TI/-Tmp-//cc1NqtRL.o 
collect2: ld returned 1 exit status

The error occurs only when I include this declaration in a file called Parameters.h:

// Parameters.h

#ifndef PARAMETERS_H
#define PARAMETERS_H

// ...[code snipped]...
const int NUM_SOCIODEM_FILES = 5;
const char * SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = { "LSPAN_PDF.txt", 
     "FLEDGE_PDF.txt", 
     "PAIR_PDF.txt", 
     "BIRTH_AGE_PDF.txt",  
     "SPLIT_PDF.txt"  };
// ...[code snipped]...
#endif

I've searched all my files, and this is the only place where SOCIODEM_FILENAMES is declared. When I comment out the declaration, the 'duplicate symbol' error goes away.

I'm unfamiliar with linker errors (if that's what this is) and would appreciate help troubleshooting the problem. All my header files have #ifndef...#define...#endif wrappers. My compile command is

g++ -o a.out -I /Applications/boost_1_42_0/ Host.cpp Simulation.cpp main.cpp Rdraws.cpp

Thanks in advance.


Solution summary

I now have in Parameters.h:

const char * const SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = { "LSPAN_PDF.txt", 
                 "FLEDGE_PDF.txt", 
                 "PAIR_PDF.txt", 
                 "BIRTH_AGE_PDF.txt",  
                 "SPLIT_PDF.txt"  };

All other definitions and declarations in Parameters.h are unchanged. Andrey and other commenters summarize an alternative approach using extern, which is overkill for my purposes.

+2  A: 

The header guard (#ifndef..#endif wrapper) just prevents you from including the same header multiple times in a single source file. You can still have multiple source files that include that header, and each one will declare that symbol separately. Since they all have the same name, linking those sources together will cause a symbol name collision. You probably want to declare the symbol in a source file instead of a header file

Michael Mrozek
+2  A: 

The problem is you are putting a definition in a header file. If you include that file in more than one compilation unit (.cpp file) you will be in effect creating multiple definitions and at link time you will get that error.

You need to put both those definitions in a .cpp file and put only a declaration in the header file:

extern const int NUM_SOCIODEM_FILES;
extern const char * SOCIODEM_FILENAMES[];
Amardeep
+3  A: 

Most likely, you are #includeing this file in multiple source files. The problem is that each inclusion results in a separate definition for a variable named SOCIODEM_FILENAMES. Include guards do not help with this. Include guards prevent multiple declarations within a single compilation unit; they do not prevent multiple definitions across several compilation units.

What you need to do is declare these variables as extern in the header, and then define them in exactly one source file. e.g.

// Parameters.h

#ifndef PARAMETERS_H
#define PARAMETERS_H

// ...[code snipped]...
extern const int NUM_SOCIODEM_FILES;
extern const char * SOCIODEM_FILENAMES[];
// ...[code snipped]...
#endif

and then:

// Parameters.cpp (or some other source file)

const int NUM_SOCIODEM_FILES = 5;    
const char * SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = { "LSPAN_PDF.txt", 
                 "FLEDGE_PDF.txt", 
                 "PAIR_PDF.txt", 
                 "BIRTH_AGE_PDF.txt",  
                 "SPLIT_PDF.txt"  };

You can get away with not doing this for the int because it is a constant integer, and so the compiler can just treat it as a compile-time constant, and it will never even show up in the compiled code. However, the char* cannot be treated this way, and so must have exactly one definition (known as the "one definition rule" in C++).

Tyler McHenry
What do you mean by the int and double parameters "never even show[ing] up in the compiled code"? All my other parameters were of constant numeric type and appeared to be present in the compiled code.
Sarah
I mean that the compiler does not necessarily need to create memory locations for storing these numbers when the program is executing. Essentially, the compiler can treat it almost as if you wrote `#define NUM_SOCIODEM_FILES 5` and just replace the variable with the literal `5` wherever it is used. The one definition rule exists so that each named variable can be mapped to a single, unique memory location. If a memory location is not required, then the issue is moot.
Tyler McHenry
@Tyler McHenry: Moving the `NUM_SOCIODEM_FILES` definition to the .cpp file is actually a bad idea. Const scalar objects are normally defined in header files. There's nothing wrong with that and actually there's quite a lot right with that. In many cases it is extremely useful to have `NUM_SOCIODEM_FILES` as an integral constant expression. This answer destroyed that valuable property of `NUM_SOCIODEM_FILES` for no reason whatsoever.
AndreyT
@Tyler McHenry: Excellent answer. I vote + for your answer.
Khnle
+2  A: 

As others have suggested, one way of doing this is to declare NUM_SOCIODEM_FILES and SOCIODEM_FILENAMES as extern and define them once in an external file. The other way is to declare them as static--this causes them to be duplicated in every object file that includes the header, but will not create an error since the definition is private to that object file. Which option you choose is entirely up to your own preferences.

JSBangs
Thanks. Hadn't known about this option.
Sarah
This is dangerous if you don't fully understand what it means -- it's creating a separate instance of those variables in every object file. It's probably fine (albeit inefficient) in this case, since they're const, but if you did this with a mutable variable and changed it in one file, only the instance in that file would change
Michael Mrozek
I wasn't planning to go this route for that reason, but I didn't realize that 'static' would create separate copies for every file. I appreciate the warning.
Sarah
@Sarah Static means the symbol is local to the object file that contains it. It "fixes" the error because now all your source files still have the same symbols with the same name, but the symbols are local instead of global so they don't collide with each other
Michael Mrozek
+6  A: 

For some reason none of the answers so far cared to explain the difference between your integer NUM_SOCIODEM_FILES object and array SOCIODEM_FILENAMES object. The latter triggers the linker error for the reasons already explained: because you include you header file into multiple implementation files. Yet, the former would link without any problems (because there are indeed no problems with NUM_SOCIODEM_FILES declaration). Why?

The reason for this is that your NUM_SOCIODEM_FILES object is declared const. In C++ const objects have internal linkage by default, meaning that they do not cause linking problems even if they are defined in multiple implementation files. In other words, in C++ your NUM_SOCIODEM_FILES is equivalent to

static const int NUM_SOCIODEM_FILES = 5; /* internal linkage */

which is why it does not lead to any linking problems.

At the same time your SOCIODEM_FILENAMES is not declared constant, which is why it gets external linkage by default and eventually leads to linker errors. Note, that if you declare your SOCIODEM_FILENAMES as const as well, the problem will go away

const char * const SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = {
  ...

Note where the extra const is placed in the declaration. If you just add that extra const and leave everything else as is (i.e. keep the definition if SOCIODEM_FILENAMES in the header file), the linker will not report the error even if you include your header file into multiple translation units.

This is, of course, not a recommended approach, since that way you will give your SOCIODEM_FILENAMES internal linkage and end up with a standalone copy of SOCIODEM_FILENAMES array in each translation unit - something that might work fine but still makes very little sense. So, for your array, it is normally better to use the extern approach recommended in other answers.

However, note that you shouldn't normally do it for NUM_SOCIODEM_FILES declaration!!! It is fine as it is, defined in the header file. Unless you are trying to do something unusual, scalar constants should normally be defined with initializer in the header files - that way they can be seen as compile-time constants in all translation units, which is a rather valuable thing to have.

Beware of the strange advices present in some other answers to move the definition of NUM_SOCIODEM_FILES into .cpp file as well - this actually makes no sense and is a totally wrong thing to do.

AndreyT
Wow, thank you. This explains a lot. This solved pretty much the exact same problem for me.
goatlinks