views:

178

answers:

6

I'm getting linkage errors of the following type:

Festival.obj : error LNK2019: unresolved external symbol "public: void __thiscall Tree::add(class Price &)" (?add@?$Tree@VPrice@@@@QAEXAAVPrice@@@Z) referenced in function __catch$?AddBand@Festival@@QAE?AW4StatusType@@HHH@Z$0

I used to think it has to do with try-catch mechanism, but since been told otherwise. This is an updated version of the question.

I'm using Visual Studio 2008, but I have similar problems in g++.

The relevant code:

In Festival.cpp

#include "Tree.h"
#include <exception>

using namespace std;

class Band{
public:
    Band(int bandID, int price, int votes=0): bandID(bandID), price(price), votes(votes){};
...
private:
...

};

class Festival{
public: 
    Festival(int budget): budget(budget), minPrice(0), maxNeededBudget(0), priceOffset(0), bandCounter(0){};
    ~Festival();
    StatusType AddBand(int bandID, int price, int votes=0);
    ...

private: 
    Tree<Band> bandTree;
    ...

};

StatusType Festival::AddBand(int bandID, int price, int votes){
    if ((price<0)||(bandID<0)){
     return INVALID_INPUT;
    }
    Band* newBand=NULL;
    try{
     newBand=new Band(bandID,price-priceOffset,votes);
    }
    catch(bad_alloc&){return ALLOCATION_ERROR;}
    if (bandTree.find(*newBand)!=NULL){
     delete newBand;
     return FAILURE;
    }
bandTree.add(*newBand);
....
}

In Tree.h:

template<class T>
class Tree{
public:
    Tree(T* initialData=NULL, Tree<T>* initialFather=NULL);
    void add(T& newData);
....
private:
....
};

Interestingly enough I do not have linkage errors when I try to use Tree functions when type T is a primitive type like an int.

A: 

Are you sure the try/catch has anything to do with it? What happens if you simply comment out the try and catch lines, leave the rest of the code as it is, and build that?

It might just be that you're missing the library that defines Tree::add(class Price &) from your link line.

RichieHindle
I tried commenting them out and the number of unresolved symbols is still the same. I'm guessing it has to do with try-catch because it says that it is a function referenced in ___catch, and that's my best guess so far.
Epsilon Vector
RichieHindle
I considered this but the function is part of Tree.h, and I do include it. The function defined is: template<class T> void Tree<T>::add(TWe call it the following way: priceTree.add(*newPriceNode);whereas priceTree is Tree<Price>, both of which are defined in the cpp file in question.
Epsilon Vector
Then I don't know what the problem is, sorry. Rebuild All? 8-)
RichieHindle
Doesn't work either...
Epsilon Vector
Perhaps flesh out a bit more of the code in the question, especially that code surrounding the use and instantiating of the templates. It sounds like you've got one of those "template defined in one compilation unit, not explicitly instantiated, and used in another compilation unit"
veefu
Added the code. Tell me if you need Tree too
Epsilon Vector
A: 

Update: using Tree functions with a primitive type doesn't result in a linking error. I updated my question in light of some of the things that were said.

Epsilon Vector
+3  A: 

Is there Tree.cpp? If there is, maybe you forgot to link it? Where is the implementation of Tree::add? In addition I don't see where you call Tree::add. I guess it should be inside the try statement, right after the new?

Just a reminder:

For most compilers (i.e. those that practice separate compilation) the implementation of the member functions of a template class has to be visible during the compilation of the source file that uses the template class. Usually people follow this rule by putting the implementation of the member functions inside the header file.

Maybe Tree::add isn't inside the header? Then a possible solution in the discussed case will be to put Tree::add implementation inside the header file.

The difference between regular classes and template classes exists because template classes are not "real" classes - it is, well, a template. If you had defined your Tree class as a regular class, the compiler could have used your code right away. In case of a template the compiler first "writes" for you the real class, substituting the template parameters with the types you supplied. Now, compiler compiles cpp files one by one. He is not aware of other cpp files and can use nothing from other cpp files. Let's say your implementation of Tree:add looks like this:

void Tree::add(T& newData)
{
    newData.destroyEverything();
}

It is totally legitimate as long as your T has method destroyEverything. When the compiler compiles Class.cpp it wants to be sure that you don't do with T anything it doesn't know. For example Tree<int> won't work because int doesn't have destroyEverything. The compiler will try to write your code with int instead of T and find out that the code doesn't compile. But since the compiler "sees" only the current cpp and everything it includes, it won't be able to validate add function, since it is in a separate cpp.

There won't be any problem with

void Tree::add(int& newData)
{
    newData.destroyEverything();
}

implemented in a separate cpp because the compiler knows that int is the only acceptable type and can "count on himself" that when he gets to compile Tree.cpp he will find the error.

FireAphis
Added the use of Tree::add, it's right at the end of the Festival segment. Yes there is Tree.cpp, and it #includes Tree.h"
Epsilon Vector
Tree add is indeed not inside the header. It's in Tree.cpp, which #includes Tree.h. Can you explain why this sort of thing (writing Class.h, Class.cpp, and then using #incliude "Class.h") is fine for none template classes/functions but doesn't work with template functions? What makes it not visible?
Epsilon Vector
Almost all existing C++ compilers require both the defclaration and the bdefinition of templates to be seen by the compiler - i.e. there is effectivelyb no linking stage for templates. You need to put the function definitions in the header file.
anon
A: 

You're getting linkage errors, not compiler errors. This tells us that the compiler knew what sort of function Tree::add() is, but didn't have a definition. In Tree.h, I see a declaration of the add() function, but not a definition. It looks odd to me; does anybody know where Tree.h came from?

Usually a template class comes with member function definitions in the include file, since the functions have to be instantiated somewhere, and the simplest thing is for the compiler to instantiate when used and let the linker sort it out. If the definitions are in Tree.h, I'd expect everything to work as planned.

So, I'm going to go out on a limb and suggest that the definitions are in a separate file, not linked in, and that there are provisions elsewhere for instantiating for basic types like Tree<int>. This is presumably to streamline compilation, as normally these things are compiled in multiple places, and that takes time.

What you need to do in that case is to find where Tree<int> is instantiated, and add an instantiation for your class.

I could be way off base here, but my explanation does fit the facts you've given.

Edit after first comments:

Templates are somewhat trickier than ordinary functions, which usually isn't a real problem. If the definitions for all the calls were in Tree.h, then Festival.cpp would be able to instantiate Tree<Band> and everything would be cool. That's the usual technique, and you're running into this problem because you're not using it.

When you write a function, it gets compiled, and the linker will find it. Any routine calling that function needs to know the function prototype, so it will know how to call it. When you write a template, you're not writing anything that will go directly into the program, but any use of the template counts as writing all the functions.

Therefore, there has to be some use of Tree<Band> somewhere in your program, for there to be a Tree<Band>::add() function compiled. The definition of Tree<T>::add has to be available to the compiler when Tree<Band> is instantiated, because otherwise the compiler has no idea what to compile. In this case, it's generating the function call, confident that you'll make sure the function is compiled elsewhere.

Therefore, you have to instantiate Tree<Band> inside a file that has access to both the definitions for Tree<T> and Band. This probably means a file that is, or includes, Tree.cpp and includes Festival.h.

The linker is already using Tree.cpp, but Tree.cpp doesn't have Tree<Band> defined in it, so it's meaningless to the linker. Templates are only useful for the compiler, and the linker only operates on what the compiler generated from templates.

The quick way to solve this is to take the definitions from Tree.cpp and put them in Tree.h. That will be likely to increase compilation and link times, unfortunately. The other technique is to instantiate all template uses in Tree.cpp, so that they'll be compiled there.

David Thornley
I have a Tree.cpp file which includes Tree.h. Is that enough for the linker? That sort of thing is usually enough for normal functions but I see now that templates are more tricky. By the way, I tried to explicitly instantiate class Tree<Band> in the Festival.cpp file and it replied saying "no suitable definition provided for explicit template instantiation request"
Epsilon Vector
Also, if it is not enough for the linker, I suppose I can try moving the implementation to the header file, but is there a way to point the linker to the cpp file?
Epsilon Vector
A: 

As others have stated you need to show the implementation of Treee::add() and tell us how you are linking it.

On an unrelated point, if you are using constructs like:

  Band* newBand=NULL;
    try{
        newBand=new Band(bandID,price-priceOffset,votes);
    }
    catch(bad_alloc&){return ALLOCATION_ERROR;}

throughout your code, you are frankly wasting your time. The chances of you getting to a point of memory exhaustion in a modern OS are remote and the chances of you doing anything useful after it has happened are roughly zero. You will be much better off simply saying:

Band * newBand = new Band ( bandID, price - priceOffset, votes );

ot possibly:

Band newBand( bandID, price - priceOffset, votes );

and forgetting the exception handling in this case.

anon
I know. This is for a university assignment.
Epsilon Vector
So you are intenionally writing pointless code for it?
anon
They ask us to handle out of memory events.
Epsilon Vector
You are not handling them. You are just transforming exceptions (which you won't get) into return codes.
anon
I know that with 0.999999 probability I'll never get these exceptions, but the assignment requires us to return ALLOCATION_ERROR in case of an allocation error, so I must have code in there that actually does this. I know that in all likelyhood it will never be invoked.
Epsilon Vector
A: 

You wrote in a comment:

I considered this but the function is part of Tree.h, and I do include it. The function defined is: template void Tree::add(T& newData); We call it the following way: priceTree.add(*newPriceNode); whereas priceTree is Tree, both of which are defined in the cpp file in question.

instead of:

priceTree.add(*newPriceNode);

try:

priceTree.add(newPriceNode); //no "*" before "newPriceNode"

add() takes a reference to a node, not a pointer to a node (according to your definition of Tree).

Eyal