1

I've been given some source code (as part of an assignment) that I'm supposed to modify, but I can't get the unmodified version of the code to compile and I'm tearing my hair out. (To be clear - this code is for a school assignment on hash tables, these compile errors are not part of the assignment)

I'm using visual studio 2010 to compile. I've been working on this all day and getting absolutely nowhere!

I'm getting a series of "LNK2005" errors:

1>------ Build started: Project: Assignment10, Configuration: Debug Win32 ------
1>  hashmain.cpp
1>e:\google drive\cpsc 1160\labs\projects\assignment10\assignment10\hashtable.cpp(40): warning C4018: '<' : signed/unsigned mismatch
1>hashtable.obj : error LNK2005: "public: __thiscall HashTableSavitch::HashTable::HashTable(void)" (??0HashTable@HashTableSavitch@@QAE@XZ) already defined in hashmain.obj
1>hashtable.obj : error LNK2005: "public: virtual __thiscall HashTableSavitch::HashTable::~HashTable(void)" (??1HashTable@HashTableSavitch@@UAE@XZ) already defined in hashmain.obj
1>hashtable.obj : error LNK2005: "private: static int __cdecl HashTableSavitch::HashTable::computeHash(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?computeHash@HashTable@HashTableSavitch@@CAHV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) already defined in hashmain.obj
1>hashtable.obj : error LNK2005: "public: bool __thiscall HashTableSavitch::HashTable::containsString(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)const " (?containsString@HashTable@HashTableSavitch@@QBE_NV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) already defined in hashmain.obj
1>hashtable.obj : error LNK2005: "public: void __thiscall HashTableSavitch::HashTable::put(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?put@HashTable@HashTableSavitch@@QAEXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) already defined in hashmain.obj
1>E:\Google Drive\CPSC 1160\Labs\Projects\Assignment10\Debug\Assignment10.exe : fatal error LNK1169: one or more multiply defined symbols found
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Here is the code I was given:

hashmain.cpp

// Program to demonstrate use of the HashTable class

#include <string>
#include <iostream>
#include "listtools.cpp"    // Your compiler may compile separately
#include "hashtable.h"
#include "hashtable.cpp"    // Your compiler may compile separately
using std::string;
using std::cout;
using std::endl;
using HashTableSavitch::HashTable;

int main()
{
    HashTable h;

    cout << "Adding dog, cat, turtle, bird" << endl;
    h.put("dog");
    h.put("cat");
    h.put("turtle");
    h.put("bird");
    cout << "Contains dog? "
        << h.containsString("dog") << endl;
    cout << "Contains cat? "
        << h.containsString("cat") << endl;
    cout << "Contains turtle? "
        << h.containsString("turtle") << endl;
    cout << "Contains bird? "
        << h.containsString("bird") << endl;

    cout << "Contains fish? "
        << h.containsString("fish") << endl;
    cout << "Contains cow? "
        << h.containsString("cow") << endl;

    return 0;
}

hashtable.h

// This is the header file hashtable.h.  This is the interface
// for the class HashTable, which is a class for a hash table
// of strings.

#ifndef HASHTABLE_H
#define HASHTABLE_H

#include <string>
#include "listtools.h"

using LinkedListSavitch::Node;
using std::string;

namespace HashTableSavitch
{
  const int SIZE = 10;

  class HashTable
  {
   public:
        HashTable(); // Initialize empty hash table

        // Normally a copy constructor and overloaded assignment
        // operator would be included.  They have been omitted
        // to save space.

        virtual ~HashTable();  // Destructor destroys hash table

        bool containsString(string target) const;
        // Returns true if target is in the hash table,
        // false otherwise

        void put(string s);
        // Adds a new string to the hash table

   private:
        Node<string> *hashArray[SIZE];
        static int computeHash(string s);   // Compute hash value for string

  }; // HashTable
} // HashTableSavitch
#endif // HASHTABLE_H

hashtable.cpp

// This is the implementation file hashtble.cpp.
// This is the implementation of the class HashTable.

#include <string>
#include "listtools.h"
#include "hashtable.h"

using LinkedListSavitch::Node;
using LinkedListSavitch::search;
using LinkedListSavitch::headInsert;
using std::string;

namespace HashTableSavitch
{
   HashTable::HashTable()
   { 
    for (int i = 0; i < SIZE; i++)
    {
     hashArray[i] = NULL;
    }
   }

   HashTable::~HashTable()
   {
     for (int i=0; i<SIZE; i++)
     {
       Node<string> *next = hashArray[i];
       while (next != NULL)
       {
         Node<string> *discard = next;
     next = next->getLink( );
     delete discard;
       }
     }
   }

   int HashTable::computeHash(string s)
   {
    int hash = 0;
    for (int i = 0; i < s.length( ); i++) 
    {
     hash += s[i];
    }
    return hash % SIZE; 
   }

   bool HashTable::containsString(string target) const
   { 
    int hash = this->computeHash(target);
    Node<string>* result = search(hashArray[hash], target);
    if (result == NULL) 
       return false;
    else
       return true;
   }

   void HashTable::put(string s)
   {
    int hash = computeHash(s);
    if (search(hashArray[hash], s)==NULL)
    {
      // Only add the target if it's not in the list
      headInsert(hashArray[hash], s);
    }
   }

} // HashTableSavitch

listtools.h

//This is the header file listtools.h. This contains type definitions and
//function declarations for manipulating a linked list to store data of any type T.
//The linked list is given as a pointer of type Node<T>* which points to the 
//head (first) node of the list. The implementation of the functions are given
//in the file listtools.cpp.
#ifndef LISTTOOLS_H
#define LISTTOOLS_H

namespace LinkedListSavitch
{
    template<class T>
    class Node
    {
    public:
        Node(const T& theData, Node<T>* theLink) : data(theData), link(theLink){}
        Node<T>* getLink( ) const { return link; }
        const T& getData( ) const { return data; }
        void setData(const T& theData) { data = theData; }
        void setLink(Node<T>* pointer) { link = pointer; }
    private:
        T data;
        Node<T> *link;
    };

    template<class T>
    void headInsert(Node<T>*& head, const T& theData);
    //Precondition: The pointer variable head points to
    //the head of a linked list.
    //Postcondition: A new node containing theData
    //has been added at the head of the linked list.

    template<class T>
    void insert(Node<T>* afterMe, const T& theData);
    //Precondition: afterMe points to a node in a linked list.
    //Postcondition: A new node containing theData
    //has been added after the node pointed to by afterMe.

    template<class T>
    void deleteNode(Node<T>* before);
    //Precondition: The pointers before point to nodes that has 
    //at least one node after it in the linked list. 
    //Postcondition: The node after the node pointed to by before
    //has been removed from the linked list and its storage 
    //returned to the freestore.

    template<class T>
    void deleteFirstNode(Node<T>*& head);
    //Precondition: The pointers head points to the first
    //node in a linked list; with at least one node.
    //Postcondition: The node pointed to by head has been removed
    //for the linked list and its storage returned to the freestore.

    template<class T>
    Node<T>* search(Node<T>* head, const T& target);
    //Precondition: The pointer head points to the head of a linked list.
    //The pointer variable in the last node is NULL. head (first) node
    //head (first) node has been defined for type T. 
    //(== is used as the criterion for being equal).
    //If the list is empty, then head is NULL.
    //Returns a pointer that points to the first node that 
    //is equal to the target. If no node equals the target, 
    //the function returns NULL.
}//LinkedListSavitch

#endif //LISTTOOLS_H

listtools.cpp

//This is the implementation file listtools.cpp. This file contains 
//function definitions for the functions declared in listtools.h.

#include <cstddef>
#include "listtools.h"

namespace LinkedListSavitch
{
    template<class T>
    void headInsert(Node<T>*& head, const T& theData)
    {
        head = new Node<T>(theData, head);
    }

    template<class T>
    void insert(Node<T>* afterMe, const T& theData)
    {
        afterMe->setLink(new Node<T>(theData, afterMe->getLink( )));
    }

    template<class T>
    void deleteNode(Node<T>* before)
    {
        Node<T> *discard;
        discard = before->getLink( );
        before->setLink(discard->getLink( ));
        delete discard;
    }

    template<class T>
    void deleteFirstNode(Node<T>*& head)
    {
        Node<T> *discard;
        discard = head;
        head = head->getLink( );
        delete discard;
    }

    //Uses cstddef:
    template<class T>
    Node<T>* search(Node<T>* head, const T& target)
    {
        Node<T>* here = head;

        if (here == NULL) //if empty list
        {
            return NULL;
        }
        else
        {
            while (here->getData( ) != target && here->getLink( ) != NULL)
                here = here->getLink( );

            if (here->getData( ) == target)
                return here;
            else
                return NULL;
        }
    }

}//LinkedListSavitch

I think that this is way out of my depth, I've poured over similar problems/solutions here and anywhere else I could find on google, but I'm completely stumped.

Edit: As per the request of Arcinde, I commented out #include "hashtable.cpp" in hashmain.cpp like so:

#include <string>
#include <iostream>
#include "listtools.cpp"    // Your compiler may compile separately
#include "hashtable.h"
//#include "hashtable.cpp"  // Your compiler may compile separately
using std::string;
using std::cout;
using std::endl;
using HashTableSavitch::HashTable;

which produces the following errors:

1>------ Build started: Project: Assignment10, Configuration: Debug Win32 ------
1>  hashmain.cpp
1>hashtable.obj : error LNK2019: unresolved external symbol "class LinkedListSavitch::Node<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > * __cdecl LinkedListSavitch::search<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >(class LinkedListSavitch::Node<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > *,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (??$search@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@LinkedListSavitch@@YAPAV?$Node@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@0@PAV10@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function "public: bool __thiscall HashTableSavitch::HashTable::containsString(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)const " (?containsString@HashTable@HashTableSavitch@@QBE_NV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)
1>hashtable.obj : error LNK2019: unresolved external symbol "void __cdecl LinkedListSavitch::headInsert<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >(class LinkedListSavitch::Node<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > * &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (??$headInsert@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@LinkedListSavitch@@YAXAAPAV?$Node@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@0@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function "public: void __thiscall HashTableSavitch::HashTable::put(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?put@HashTable@HashTableSavitch@@QAEXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)
1>E:\Google Drive\CPSC 1160\Labs\Projects\Assignment10\Debug\Assignment10.exe : fatal error LNK1120: 2 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
valiano
  • 16,433
  • 7
  • 64
  • 79
b1skit
  • 327
  • 4
  • 16
  • Template function implementation must be visible at template instantiation point. Which essentially means all template code must be in .h file. – n0rd Jul 25 '15 at 00:15
  • I uncommented the 2 .cpp files in hashmain.cpp, which changed the error output considerably. – b1skit Jul 25 '15 at 00:39
  • Try commenting out only the `#include "hashtable.cpp"` and leave the `#include "listtools.cpp"` uncommented, that should fix it. And tell whoever wrote this what @n0rd said..this is pretty badly organized. – jcai Jul 25 '15 at 00:43
  • Commenting out the ` #include "hashtable.cpp" ` gives me 2 `error LNK2019: unresolved external symbol` errors (both mentioning `LinkedListSavitch`" – b1skit Jul 25 '15 at 00:51
  • Hmm, can you post the full errors? You are definitely including listtools.cpp and leaving out hashtable.cpp? – jcai Jul 25 '15 at 01:31
  • Yeah, I'm certain that I'm including listtools.cpp and leaving out hashtable.cpp . I've updated the OP to include the full error messages, hope it helps – b1skit Jul 25 '15 at 01:54
  • Make a backup first before attempting my changes. Cut the entire contents of listtools.cpp and paste it to listtools.h right before the #endif. Delete the #include listtools.h line from what you just pasted, and move the #include line to the top of the file right after the #define LISTTOOLS_H. Finally, you can delete listtools.cpp and remove the #include listtools.cpp from hashmain.cpp. – jcai Jul 25 '15 at 02:34
  • I merged all of the function/class definitions and implementation into 1 .cpp file (I omitted the namespace definitions etc) and the (unchanged) code compiles. Thanks so much for your support! I wish I understood exactly what the issue was, but for now, I'll take it. – b1skit Jul 25 '15 at 17:29

3 Answers3

2

It looks like the root of your problems was that you were instantiating template functions without an implementation (your .h was attempting to declare, and your .cpp was attempting to define them).

You have two options to resolve your issue:

  1. Move everything from listtools.cpp into listtools.h, thus moving both the template instantiation and implementation to the same place
  2. Move everything from listtools.cpp into a new file (listtoolssupport.h) and remove the declarations from listtools.h.

Using option 1, I moved all the implementations of listtools.cpp into listtools.h using @n0rd's and @ Arcinde's instructions. I then completely removed the listtools.cpp as it now did nothing and removed the #includes of the .cpp.

listtools.h now looks like:

//This is the header file listtools.h. This contains type definitions and
//function declarations for manipulating a linked list to store data of any type T.
//The linked list is given as a pointer of type Node<T>* which points to the 
//head (first) node of the list. The implementation of the functions are given
//in the file listtools.cpp.
#ifndef LISTTOOLS_H
#define LISTTOOLS_H
#include <cstddef>
namespace LinkedListSavitch
{
template<class T>
class Node
{
public:
    Node(const T& theData, Node<T>* theLink) : data(theData), link(theLink) { }
    Node<T>* getLink() const { return link; }
    const T& getData() const { return data; }
    void setData(const T& theData) { data = theData; }
    void setLink(Node<T>* pointer) { link = pointer; }
private:
    T data;
    Node<T> *link;
};

template<class T>
void headInsert(Node<T>*& head, const T& theData)
{
    head = new Node<T>(theData, head);
}
//Precondition: The pointer variable head points to
//the head of a linked list.
//Postcondition: A new node containing theData
//has been added at the head of the linked list.

etc etc etc...

The "Unresolved External Symbol" is basically saying that, when it goes to link, it cannot find a symbol for Node.

Kevin K
  • 408
  • 2
  • 18
-1

In your hashmain.cpp file

// Program to demonstrate use of the HashTable class

#include <string>
#include <iostream>
//#include "listtools.cpp"  // Your compiler may compile separately
#include "hashtable.h"
//#include "hashtable.cpp"  // Your compiler may compile separately
using std::string;
using std::cout;
using std::endl;
using HashTableSavitch::HashTable;

Try uncommenting the two commented lines.

jcai
  • 3,448
  • 3
  • 21
  • 36
  • Done. I think I may have commented them out by myself, uncommenting them changed the error outputs considerably so I have updated the question. – b1skit Jul 25 '15 at 00:34
  • Thank you for this suggestion, which might provide some limited, immediate help. A proper explanation [would greatly improve](//meta.stackexchange.com/q/114762) its long-term value by showing *why* this is a good solution to the problem, and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you've made. – Toby Speight Oct 12 '17 at 08:41
-1

It is wrong that include source code file name, so you should commented them.

JerryYoung
  • 267
  • 1
  • 3