Throwing traps causes clutch errors
I am getting chaining errors of the following type:
Festival.obj: bug LNK2019: unresolved external symbol "public: void __thiscall Tree :: add (class Price &)" (? Add @? $ Tree @VPrice @@@@ QAEXAAVPrice @@@ Z) link in function __catch $? AddBand @Festival @@ QAE? AW4StatusType @@ HHH @Z $ 0
I used to think it had something to do with the try-catch mechanism, but it was said otherwise. This is an updated version of the question.
I am using Visual Studio 2008 but I have similar problems in g ++.
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, I have no binding errors when I try to use Tree functions when T is a primitive type of int type.
a source to share
Is there a Tree.cpp? If there is, perhaps you forgot to tie it? Where is the implementation of Tree :: add? Also, I can't see where you are calling Tree :: add. I'm guessing it should be inside the try statement right after the new one?
Just a reminder:
For most compilers (that is, those who practice separate compilation), the implementation of the member functions of the template class should be visible at compile time to the source file that uses the template class. Usually people follow this rule by putting the implementation of member functions inside a header file.
Maybe Tree :: add is not inside the header? Then a possible solution in the case under discussion would include implementing Tree :: add inside the header file.
The difference between regular classes and templates is because template classes are not "real" classes - they are, well, a template. If you defined your Tree class as a regular class, the compiler could use your code right away. In the case of a template, the compiler first "writes" the real class for you, replacing the template parameters with the types you specified. Now the compiler compiles the cpp files one by one. It doesn't know any other cpp files and can't use any of the other cpp files. Let's say your implementation of Tree: add looks like this:
void Tree::add(T& newData)
{
newData.destroyEverything();
}
It's completely legal if your T has a destroyEverything method. When the compiler compiles Class.cpp, it wants to make sure you don't do anything with T that 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 won't compile. But since the compiler only "sees" the current cpp and everything it includes, it will not be able to check the add function as it is in a separate cpp.
There will be no 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 itself" that when it builds Tree.cpp it will encounter an error.
a source to share
You are getting communication errors, not compiler errors. This tells us that the compiler knew which function Tree::add()
is, but has no definition. In Tree.h, I see the add () function declaration, but not the definition. This seems strange to me; does anyone know where Tree.h came from?
Typically a template class comes with member function definitions in an include file, since the functions have to be instantiated somewhere, and the simplest is to instantiate the compiler when used and let the linker sort it. If the definitions are in Tree.h, I expect everything to work as planned.
So I'm going to go to the finiteness and assume that the definitions are in a separate file, unrelated, and that conditions exist elsewhere to create base types of a type Tree<int>
. Presumably this makes it easier to compile as usually these things are compiled in multiple places and it takes time.
What you need to do in this case is find where the Tree<int>
instance is being created and add an instance for your class.
I could step back from the base, but my explanation matches your actions.
Edit after first comments :
Templates are somewhat more complex than regular functions, which is usually not a real problem. If the definitions for all the calls were in Tree.h, then Festival.cpp could instantiate it Tree<Band>
, and everything would be cool. This is a common technique and you run into this problem because you are not using it.
When you write a function, it will compile and the linker will find it. Any procedure that calls this function must know the prototype of the function, so it will know how to call it. When you write a template, you don't write anything that goes directly into the program, but any use of the template counts as a record of all functions.
Hence there must be some use Tree<Band>
somewhere in your program as the function must be compiled Tree<Band>::add()
. The definition Tree<T>::add
must be available to the compiler when instantiated Tree<Band>
, because otherwise the compiler has no idea how to compile. In this case, it generates a function call, making sure you make sure the function is compiled elsewhere.
Therefore, you need to create an instance Tree<Band>
inside a file that has access to the definitions for Tree<T>
and Band
. This probably means the file that contains the Tree.cpp or includes the Festival.h file.
The component already uses Tree.cpp, but Tree.cpp doesn't have Tree<Band>
one defined in it, so it doesn't make sense to the linker. Templates are only useful to the compiler, and the linker only works with what the compiler generates from templates.
A quick way to solve this is to take the definitions from Tree.cpp and put them in Tree.h. Unfortunately, this will likely increase compile and link times. Another method is to instantiate all uses of the templates in Tree.cpp so that they will be compiled there.
a source to share
As others have stated, you need to show the implementation of Treee :: add () and tell us how you are linking it.
At an unrelated point, if you use constructs like:
Band* newBand=NULL;
try{
newBand=new Band(bandID,price-priceOffset,votes);
}
catch(bad_alloc&){return ALLOCATION_ERROR;}
throughout the code, you are frankly wasting your time. The chances of you getting to memory depletion in a modern OS are far, and the chances of you doing anything useful after it has happened are roughly nil. You'd be much better off saying:
Band * newBand = new Band ( bandID, price - priceOffset, votes );
possibly:
Band newBand( bandID, price - priceOffset, votes );
and forget about handling exceptions in this case.
You wrote in a comment:
I considered it, but the function is part of Tree.h and I include it. Defined function: template void Tree :: add (T & newData); We call it this way: priceTree.add (* newPriceNode); while priceTree is a Tree, both of which are defined in the cpp file in question.
instead:
priceTree.add(*newPriceNode);
try:
priceTree.add(newPriceNode); //no "*" before "newPriceNode"
add () takes a reference to a node, not a pointer to a node (as per your tree definition).
a source to share