views:

323

answers:

3

Hello all,

Using g++ and having linker errors. I have a simple program in split into two modules: main.cpp and Dice.h Dice.cpp.

main.cpp:

#include <iostream>
#include "Dice.h"

int main(int argc, char **argv) {

    int dieRoll = Dice::roll(6);
    std::cout<<dieRoll<<std::endl;

    std::cin.get();

    return 0;
}

Dice.h:

#ifndef DieH
#define DieH

namespace Dice
{
    int roll(unsigned int dieSize);
}

#endif

Dice.cpp:

#include <ctime>
#include <cstdlib>
#include "Dice.h"

namespace Dice
{
    int roll(unsigned int dieSize)
    {
     if (dieSize == 0)
     {
      return 0;
     }
     srand((unsigned)time(0));
     int random_int = 0;
     random_int = rand()%dieSize+1;

     return random_int;
    }
}

I compile and link these files using g++ as follows:

g++ -o program main.cpp Dice.cpp

And I get the following linker error:

Undefined symbols:
"Dice::roll(int)", referenced from:
  _main in ccYArhzP.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

I'm completely flummoxed. Any help would be greatly appreciated.

+1  A: 

Your problem is that you're calling Dice::roll(int) and you wrote a Dice::roll(unsigned int) function. The function it's looking for doesn't actually exist (internally, argument types of a function matter just as much as its name; this is how overloading works). Try passing it an unsigned int (6u for example) and see if that works. Alternately, your header file may not match in both files, and main.cpp thinks your function takes a (signed) int.

pumpkin
That would not make a difference. The compiler can implicitly cast.
GMan
I think Ken just didn't copy-and-paste his header, but rather rewrote it from scratch here, and that's the problem with `int`/`unsigned int`.
Pavel Shved
It can, but given the error message it doesn't appear to be trying that (and given the separate compilation units, how would the first one know to try two separate versions of the symbol?). Signed and unsigned types in g++ at least are mangled differently (the unsigned ones typically get a U on them) so it seems like it could be a problem.
pumpkin
Passing it a different type would not change it though, because the compiler would still assume that the function takes an `int`, and cast it back to what the presumably missing function is expecting.
GMan
I never realized it was that smart. I guess you learn something every day :) I updated the post with the only other explanation that makes sense to me.
pumpkin
Pavel - you're right on the money. I made an error in Dice.h. please see Gman's post above.
Ken
GCC is very smart - especially the latest 4.4.x release track. It surprises me every single day by outputting code that looks like as if it was output by an experienced assembly programmer.
LiraNuna
A: 

I know you don't want to hear anything like it, but "It works for me!".

Your errors look suspiciously old - what version of GCC are you using? This look like GCC 2.x! Also, try to type dieRoll in main.cpp as unsigned, that might resolve namespace problem on this old compiler.

LiraNuna
I'm using gcc version 4.2.1. Platform is Mac OS X 10.6.1. Using the command prompt instead of XCode.
Ken
Apple truly made GCC 4 look like GCC 2.95. GCC's error messages are nothing like those today.
LiraNuna
The errors are from ld so the version of gcc is irrelevant.
R Samuel Klatchko
Oh that's right, that's not gold, that's odcctools! They look like a fork of a really old binutils. Shame on me for taking binutils+gcc for granted!
LiraNuna
+8  A: 

Your code is well-formed.

Ensure that your don't have conflicting file names, the files exist and contain what you think they do. For example, perhaps you have a Dice.cpp that's empty, and you're editing a newly created one somewhere else.

Minimize possible discrepancy by removing unnecessary files; only have main.cpp, dice.h, and dice.cpp.

Your errors do not match your code: "Dice::roll(int)". Observe that this is looking for an int, but your functions take an unsigned int. Make sure your header matches.


Try the following:

g++ main.cpp -c

This will generate main.o, the compiled but not-linked code for main. Do the same with dice.cpp:

g++ dice.cpp -c

You now have two object files that need to be linked together. Do so with:

g++ main.o dice.o

And see if that works. If not, do the following:

nm main.o dice.o

This will list all the available symbols in an object, and should give you something like this:

main.o:
00000000 b .bss
00000000 d .ctors
00000000 d .data
00000000 r .eh_frame
00000000 t .text
00000098 t __GLOBAL__I_main
00000069 t __Z41__static_initialization_and_destruction_0ii
         U __ZN4Dice4rollEj
         U __ZNSi3getEv
         U __ZNSolsEPFRSoS_E
         U __ZNSolsEi
         U __ZNSt8ios_base4InitC1Ev
         U __ZNSt8ios_base4InitD1Ev
         U __ZSt3cin
         U __ZSt4cout
         U __ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
00000000 b __ZStL8__ioinit
         U ___gxx_personality_v0
         U ___main
00000055 t ___tcf_0
         U _atexit
00000000 T _main

dice.o:
00000000 b .bss
00000000 d .data
00000000 t .text
00000000 T __ZN4Dice4rollEj
         U _rand
         U _srand
         U _time

C++ mangles function names, which is why everything looks so weird. (Note, there is no standard way of mangling names, this is how GCC 4.4 does it).

Observe that dice.o and main.o refer to the same symbol: __ZN4Dice4rollEj. If these do not match, that's your problem. For example, if I change part of dice.cpp to be this:

// Note, it is an int, not unsigned int
int roll(int dieSize)

Then nm main.o dice.o produces the following:

main.o:
00000000 b .bss
00000000 d .ctors
00000000 d .data
00000000 r .eh_frame
00000000 t .text
00000098 t __GLOBAL__I_main
00000069 t __Z41__static_initialization_and_destruction_0ii
         U __ZN4Dice4rollEj
         U __ZNSi3getEv
         U __ZNSolsEPFRSoS_E
         U __ZNSolsEi
         U __ZNSt8ios_base4InitC1Ev
         U __ZNSt8ios_base4InitD1Ev
         U __ZSt3cin
         U __ZSt4cout
         U __ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
00000000 b __ZStL8__ioinit
         U ___gxx_personality_v0
         U ___main
00000055 t ___tcf_0
         U _atexit
00000000 T _main

dice.o:
00000000 b .bss
00000000 d .data
00000000 t .text
00000000 T __ZN4Dice4rollEi
         U _rand
         U _srand
         U _time

Note, this gives two different symbols. main.o looking for this: __ZN4Dice4rollEj and dice.o containing this __ZN4Dice4rollEi. (The last letter differs).

When trying to compile these mismatched symbols (with g++ main.o dice.o), I get:

undefined reference to `Dice::roll(unsigned int)'
GMan
Checked and double checked. Only three files in the directory. The program compiles, but fails to link. Same error as originally stated.
Ken
That's pretty wild. I'm going to write up some steps we'll take.
GMan
I'd suggest compiling the two files separately, running nm on their .o files and piping that through c++filt. main.o should contain an undefined symbol for your Dice::roll(?) and Dice.o should contain a defined one for possibly a different Dice::roll.
pumpkin
Was in progress of that, pumpkin :P Geez that feels weird to type, that's what I call my girlfriend...
GMan
Many thanks. I made an error in Dice.h. Please see GMan's posts above.
Ken
Oh, I had just finished showing you how to look at symbols too :) You certainly saw more of C++ than I did when I started.
GMan