0

Possible Duplicate:
Why can templates only be implemented in the header file?

I have a singly linked list that inserts new book titles alphabetically and also deletes them. I am now trying to convert this to a template program so that other object other than Book can be utilized. I have worked through all errors but am still failing at build with the following:

Undefined symbols for architecture x86_64:
  "ObjectList<Book>::insert(Book*)", referenced from:
      _main in lib.o ,br>
  "ObjectList<Book>::getObjectList(char*)", referenced from:
      _main in lib.o 
  "ObjectList<Book>::delet(Book*)", referenced from:
      _main in lib.o 

I'm not quite sure what I have done wrong so here is the code:

//--------------------------------------------------------------
//  lib.cpp
//  
//--------------------------------------------------------------

#include <iostream>
using namespace std;
#include "ObjectList.h"
#include "Book.h"

int main(int argc, char* argv[]) {
//--------------------------------------------------------------
//  Creates a BookList object, adds several books to the list,
//  then prints them.
//--------------------------------------------------------------

   char list[2048];
   ObjectList<Book> *books = new ObjectList<Book>();

   books->insert (new Book("F Title"));
   books->insert (new Book("D Title"));
   books->insert (new Book("G Title"));
   books->insert (new Book("A Title"));
   books->insert (new Book("E Title"));
   books->insert (new Book("H Title"));

   cout << "After inserts:\n";
   cout << books->getObjectList(list) << endl;;
//*/
   books->delet (new Book("A Title"));
   books->delet (new Book("H Title"));
   books->delet (new Book("G Title"));
   books->delet (new Book("E Title"));

   cout << "After deletes:\n";
   cout << books->getObjectList(list) << endl;;

   books->insert (new Book("A Title"));
   books->insert (new Book("E Title"));
   books->insert (new Book("H Title"));
   books->insert (new Book("G Title"));

   cout << "After 2nd inserts:\n";
   cout << books->getObjectList(list) << endl;;
//*/
   return 0;
}

/*/ When running successfully this should be the output:

After inserts:
A Title
D Title
E Title
F Title
G Title
H Title

After deletes:
D Title
F Title

After 2nd inserts:
A Title
D Title
E Title
F Title
G Title
H Title

ObjectList.h

//********************************************************************
//  ObjectListt.h
//
//  Represents a collection of books.
//*******************************************************************

#include <iostream>
using namespace std;

template<class T>
class ObjectNode {
   public:
      //--------------------------------------------------------------
      //  Sets up the node
      //--------------------------------------------------------------
      ObjectNode() {}
      ObjectNode(T *theObject) {
         object = theObject;
         next = NULL;
      }
      friend class ObjectList;

   private:
      T *object;
      ObjectNode *next;
};

template<class T>
class ObjectList {

   //----------------------------------------------------------------
   //  Sets up an empty list of books.
   //----------------------------------------------------------------
   public:
      void add(T *);
      void insert(T *);
      void delet(T *);
      char* getObjectList(char *);

      ObjectList() {
         head = NULL;
      }

   private:
      ObjectNode<T> *head;

 };

Book.h

#include <cstring>
#include <iostream>
using namespace std;

//********************************************************************
//  Book.h
//
//  Represents a single book.
//*******************************************************************

class Book {

   public:
      Book (char *newTitle) {
         strcpy( title, newTitle );
      }

      int compareTo(Book *test_book)
      {
          // comparing test_book to this book
          int comparison;

          comparison = strcmp(test_book->getObject(), title);

          return comparison;
      }
//*/
      char *getObject() {
         return title;
      }

   private:
      char title[81];

   };

This program worked just fine when it was not using templates. I did not include the code for ObjectList.cpp as it is about 160 lines long and did not think it would be entirely necessary to include. Let me know if you need to see it.

Any amount of help is appreciated for this most likely rookie mistake.

Hardware info: 2011 15" MacBook Pro Running OS X Lion Netbeans IDE with all updates

Community
  • 1
  • 1
  • The implementation of all the methods of your template `ObjectList` class must be in the header file (or at least included some way or another when you use the `ObjectList` class) – Mat Nov 15 '11 at 20:34
  • 3
    Is there any reason not to use `std::list<>`? – David Thornley Nov 15 '11 at 20:35
  • I was going to suggest a std::list too but this behave more like a std::set as the Books are stored in alphabetic order. – Joel Nov 15 '11 at 20:52
  • @DavidThornley: It's got to be homework or practice. – Ed S. Nov 15 '11 at 20:57
  • 1
    Horrible C++ code. Looks like you are trying to write Java using C++. – Martin York Nov 15 '11 at 21:12
  • Oh, and `using namespace std` in a header isn't great style either. If you were actually using anything from that namespace in the header, it's better to just qualify it explicitly (like `std::cout`). – Useless Nov 16 '11 at 08:33

3 Answers3

4

Presumably you have, in ObjectList.cpp, the definition of

template <typename T> void ObjectList<T>::insert(T *)
{ // ...
}

right? Well, the problem is the compiler can't generate code for that without knowing the value passed in as T, which generally means where the method is called.

If you move all the method bodies of your templated classes inline (into the headers), it'll work. This is the usual way of writing C++ templates: it all lives in the header.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • 3
    Well, you can move the implementations to the separate file, but you still have to include it in the header, or export explicit instantiations. – Cat Plus Plus Nov 15 '11 at 20:39
  • +1. Just add the implementation of ObjectList member functions in the header. You can inline the implementation along with the declaration or declare you class and define the implementation but since ObjectList is a template class it has to all be in the header. – Joel Nov 15 '11 at 20:57
1

The compiler must have access to the definition of template function in order to generate an appropriate implementation for a given value of T. Idiomatically template functions are defined in the header file. They can be defined elsewhere but you will need to include that file as well wherever you use them. Best to just stick it all in the header.

Ed S.
  • 122,712
  • 22
  • 185
  • 265
0

Rename your ObjectList.cpp to ObjectList.tpp and add this line at the very bottom of ObjectList.h

#include "ObjectList.tpp"

This is a hack that allows you to keep your implementation in a separate file, yet ensures that anyone who includes your header file also gets the implementation (since the template system requires such things).

Here are a list of other things you are going to want to take a look at:

  1. you will want to make sure that any object that you new is also passed to delete.
  2. It is a great idea to use header inclusion guards (just be sure that the #include I mention above is INSIDE the inclusion guard in ObjectList.h).
  3. Avoid strcpy like the plague. It is very hard to use correctly. You should prefer to use classes and functions from the <string> header file instead (unless you have very specific reasons for not doing so, i.e. performance)
Michael Price
  • 8,088
  • 1
  • 17
  • 24