1

Problem fixed. Thanks a lot!

I am having the following error in the code shown below:

Error is as follows:

$ g++ main.cpp Neighbor.cpp Graph.cpp
/tmp/ccclDcUN.o: In function main':
main.cpp:(.text+0xc1): undefined reference to
Graph::add(int, Neighbor&)'
main.cpp:(.text+0xd3): undefined reference to `Graph::add(int, Neighbor&)'
collect2: ld returned 1 exit status

what could be going wrong?

// FILENAME: Graph.cpp
#include "Neighbor.h"
#include "Graph.h"

template <typename NS>
void Graph<NS>::add(int id,NS& n){
    if(id>=adj_list.size())
        while(adj_list.size()<id+1)
            adj_list.push_back(list<NS>());
    adj_list[id].push_back(n);
}


template <typename NS>
void Graph<NS>::remove(int id,NS& n){
    if(id<adj_list.size()){
        adj_list[id].remove(n);
    }
}


// FILENAME: Graph.h
#ifndef GRAPH_H
#define GRAPH_H

#include "utils.h"
#include <vector>
#include <list>

class Neighbor;

template <typename NS>
class Graph {
    private:
        std::vector<std::list<NS> > adj_list;
    public:
        void add(int,NS&);
        void remove(int,NS&);
        inline typename std::vector<std::list<NS> >::iterator begin() { return adj_list.begin(); }
        inline typename std::vector<std::list<NS> >::iterator end() { return adj_list.end(); }
};

#endif


// FILENAME: Neighbor.cpp
#include "Neighbor.h"
#include <iostream>

Neighbor::Neighbor(int id,float e,float p):id(id),edge_cost(e),price(p){}

bool operator==(const Neighbor& n1,const Neighbor& n2) {
    if(&n1==&n2) return true;
    return false;
}

ostream& operator<<(ostream& ostr,const Neighbor& n1) {
    ostr<<"["<<n1.id<<","<<n1.price<<","<<n1.edge_cost<<"]";
    return ostr;
}


// FILENAME: Neighbor.h
#ifndef NEIGHBOR_H
#define NEIGHBOR_H

#include <iosfwd>


class Neighbor {
    private:
        int id;
        float edge_cost;
        float price;
    public:
        Neighbor(int,float,float p=0.0);
        friend bool operator==(const Neighbor&,const Neighbor&);
        friend std::ostream& operator<<(std::ostream&,const Neighbor&);
};

#endif


// FILENAME: utils.h
#ifndef UTILS_H
#define UTILS_H

#include <iostream>
#include <fstream>
#include <stack>
#include <queue>
#include <vector>
#include <list>
#include <string>
#include <algorithm>

namespace utility {

typedef std::pair<int,int> ii;
typedef std::vector<int> vi;
typedef std::vector<ii> vii;
typedef std::vector<vii> vvii;
typedef std::stack<int> si;
typedef std::queue<int> qi;

}

#define UTILITY_TR(c,i) for(typeof((c).begin()) i = (c).begin() ; i!=(c).end() ; ++i )
#define UTILITY_ALL(c) (c).begin(),(c).end()
#define UTILITY_CPRESENT(c,x) (find(all(c),x) != (c).end())

#endif

// FILENAME: main.cpp
#include "utils.h"
#include "Neighbor.h"
#include "Graph.h"

using namespace std;

int main() {
    Graph<Neighbor> graph;
    Neighbor n1(1,10);
    Neighbor n2(0,10);
    graph.add(0,n1);
    graph.add(1,n2);

    cout<<"Printing graph"<<endl;
    cout<<"--------------"<<endl;

    UTILITY_TR(graph,it) {
        UTILITY_TR(*it,n) {
            cout<<*n<<endl;
        }
    }
};
user855
  • 19,048
  • 38
  • 98
  • 162
  • 2
    When we suggested before that you not use `using namespace std` in header files and that you use meaningful names for types, we were serious. – James McNellis Nov 22 '09 at 23:05
  • damn! How did I miss that one line! Sorry! – user855 Nov 22 '09 at 23:11
  • And, this is a duplicate; see this question: http://stackoverflow.com/questions/1639797/template-issue-causes-linker-error-c (especially GMan's yummy explanation). – James McNellis Nov 22 '09 at 23:14
  • Thank you very much! This was indeed a subtle thing. I must admit, I am just starting to use templates. Hence all this trouble. Talking about moving the definition to the .h file for function templates, I've seen some source code where the developers put all of their code in .h files. And there is just a single .cpp file .. the main.cpp. Is this a good code organization approach? – user855 Nov 22 '09 at 23:27
  • 1
    All their code? Definitely not good. Its only common to put the definition of class templates and function templates into headers. – Georg Fritzsche Nov 23 '09 at 02:13

2 Answers2

3

What I usually do is manually verify the symbol exists in the library:

objdump --syms foo.o

This will output a list of symbols contained in the .o file... (since it's a link error, you should have .o files... (make sure you pass -c to g++ to get it to stop after compilation))... Then you can just visually verify the object has the symbols you think it does...

nobody
  • 19,814
  • 17
  • 56
  • 77
dicroce
  • 45,396
  • 28
  • 101
  • 140
1

You need to have the definition of Graph's functions (add and remove) in the .h file so that the linker can find it.

I try to think of templates like envelopes. It's nonsensical to send it (compile) before you put in a letter (defined type). Seeing as cpp files are what is compiled, it makes sense that there shouldn't be cpp files for templated types.

HTH!

0xC0DEFACE
  • 8,825
  • 7
  • 34
  • 35