template compile errors

T

Thomi Richards

Hi,


I'm trying to create a simple stack class using C++ and templates.
Everything works well and good if I amke the class totally inline.
However, as soon as I try to seperate the class into a .h and a .cpp
file, gcc throws some strange linking errors.

Compiling like this:

gcc test.cpp stackList.cpp -lstdc++ -o test

produces these errors:

/tmp/ccCJLrbl.o(.text+0x84): In function `main':
: undefined reference to `Stack<int>::Stack[in-charge]()'
/tmp/ccCJLrbl.o(.text+0x101): In function `main':
: undefined reference to `Stack<int>::push(int)'
/tmp/ccCJLrbl.o(.text+0x10c): In function `main':
: undefined reference to `Stack<int>::size() const'
/tmp/ccCJLrbl.o(.text+0x18b): In function `main':
: undefined reference to `Stack<int>::empty() const'
/tmp/ccCJLrbl.o(.text+0x19c): In function `main':
: undefined reference to `Stack<int>::size() const'
/tmp/ccCJLrbl.o(.text+0x1aa): In function `main':
: undefined reference to `Stack<int>::pop()'
/tmp/ccCJLrbl.o(.text+0x20f): In function `main':
: undefined reference to `Stack<int>::~Stack [in-charge]()'
/tmp/ccCJLrbl.o(.text+0x229): In function `main':
: undefined reference to `Stack<int>::~Stack [in-charge]()'
collect2: ld returned 1 exit status


I'm tearing my hair out... why would it not work in seperate files? My
class definition is something like this:

template <class T>
class Stack
{
private:
StackNode<T> *stackHead;
/// The number of elements on the stack. It's easier to keep count
/// of them here than to count them every time we need them.
unsigned int stackSize;
protected:
public:
/// Default constructor: set the stackSize to be 0.
Stack(void);

/// Default destructor: clean up any memory allocated.
~Stack(void);
/// push: push an item onto the stack, and incrememt the size.
bool push(T item);

/// pop: return the topmost element from the stack, removing it
from the stack at the same time:
T pop(void);

/// size: returns how many items are on the stack:
unsigned int size(void) const;
/// empty: return true if the stack is empty:
bool empty(void) const;
/// full: return true if the stack is full: - for a linked list
implementation, this can be safely ignored:
bool full(void) const;

};


and the code for a single method for the above class looks like this:

template <class T> bool Stack<T>::push(T item)
{
if (full())
return false;

StackNode<T> *node = new StackNode<T> (item);
node->Head = stackHead;
stackHead = node;
stackSize++;

return true;
}

I've been following the C++ language tutorial, and a few books I own
at home. It looks like I'm doing everything properly... Can anyone
shed some light on this?


Thanks,
 
D

David Lindauer

Thomi said:
Hi,

I'm trying to create a simple stack class using C++ and templates.
Everything works well and good if I amke the class totally inline.
However, as soon as I try to seperate the class into a .h and a .cpp
file, gcc throws some strange linking errors.

Compiling like this:

gcc test.cpp stackList.cpp -lstdc++ -o test

produces these errors:

/tmp/ccCJLrbl.o(.text+0x84): In function `main':
: undefined reference to `Stack<int>::Stack[in-charge]()'
/tmp/ccCJLrbl.o(.text+0x101): In function `main':
: undefined reference to `Stack<int>::push(int)'
/tmp/ccCJLrbl.o(.text+0x10c): In function `main':
: undefined reference to `Stack<int>::size() const'
/tmp/ccCJLrbl.o(.text+0x18b): In function `main':
: undefined reference to `Stack<int>::empty() const'
/tmp/ccCJLrbl.o(.text+0x19c): In function `main':
: undefined reference to `Stack<int>::size() const'
/tmp/ccCJLrbl.o(.text+0x1aa): In function `main':
: undefined reference to `Stack<int>::pop()'
/tmp/ccCJLrbl.o(.text+0x20f): In function `main':
: undefined reference to `Stack<int>::~Stack [in-charge]()'
/tmp/ccCJLrbl.o(.text+0x229): In function `main':
: undefined reference to `Stack<int>::~Stack [in-charge]()'
collect2: ld returned 1 exit status

I'm tearing my hair out... why would it not work in seperate files? My
class definition is something like this:

template <class T>
class Stack
{
private:
StackNode<T> *stackHead;
/// The number of elements on the stack. It's easier to keep count
/// of them here than to count them every time we need them.
unsigned int stackSize;
protected:
public:
/// Default constructor: set the stackSize to be 0.
Stack(void);

/// Default destructor: clean up any memory allocated.
~Stack(void);
/// push: push an item onto the stack, and incrememt the size.
bool push(T item);

/// pop: return the topmost element from the stack, removing it
from the stack at the same time:
T pop(void);

/// size: returns how many items are on the stack:
unsigned int size(void) const;
/// empty: return true if the stack is empty:
bool empty(void) const;
/// full: return true if the stack is full: - for a linked list
implementation, this can be safely ignored:
bool full(void) const;

};

and the code for a single method for the above class looks like this:

template <class T> bool Stack<T>::push(T item)
{
if (full())
return false;

StackNode<T> *node = new StackNode<T> (item);
node->Head = stackHead;
stackHead = node;
stackSize++;

return true;
}

I've been following the C++ language tutorial, and a few books I own
at home. It looks like I'm doing everything properly... Can anyone
shed some light on this?

Thanks,

code for a template doesn't really exist until you instantiate it... in
a very raw sense they are kind of like
macros in that regard. Until you use them, no code or data gets
generated. If you just take part of a group of template definitions and
stick them in a C++ file, no code will be generated in the file, and you
end up with essentially the same problem as if you are working in C and
move all the global #defines into a C language source file, they just
wouldn't be accessible. What happens when you instantiate a template is,
the compiler takes all the structures and functions you have declared
related to the template and tries to make functions out of them, using the
instantiated types instead of the placeholder types you used when you
declared the template. So you can instantiate one template using many
many different types in the course of a single program, but the cost is
that each time you change the types given in the instantiation, the
compiler generates another set of code and data specific to those types.

You might be able to fix this particular problem by instantiating a
template using the <int> type in your source file that you put the
templates in, that may at least cause the functions to get declared so
they will appear at link time. However, that means that for every time
you want to use that template with a different type, you've got to go into
your source file and instantiate it deliberately. Since that really cuts
down on the power of templates dramatically, it is usually recommended you
just group all the template functions and so forth in header files, but of
course that is its own problem since if you are modifying global headers
very often it can force you to recompile large parts of your project on a
regular basis.

David
 
E

E. Robert Tisdale

Thomi said:
I'm trying to create a simple stack class using C++ and templates.
Everything works well and good if I amke the class totally inline.
However, as soon as I try to seperate the class into a .h and a .cpp
file, gcc throws some strange linking errors.

Compiling like this:

gcc test.cpp stackList.cpp -lstdc++ -o test

produces these errors:

/tmp/ccCJLrbl.o(.text+0x84): In function `main':
: undefined reference to `Stack<int>::Stack[in-charge]()'
/tmp/ccCJLrbl.o(.text+0x101): In function `main':
: undefined reference to `Stack<int>::push(int)'
/tmp/ccCJLrbl.o(.text+0x10c): In function `main':
: undefined reference to `Stack<int>::size() const'
/tmp/ccCJLrbl.o(.text+0x18b): In function `main':
: undefined reference to `Stack<int>::empty() const'
/tmp/ccCJLrbl.o(.text+0x19c): In function `main':
: undefined reference to `Stack<int>::size() const'
/tmp/ccCJLrbl.o(.text+0x1aa): In function `main':
: undefined reference to `Stack<int>::pop()'
/tmp/ccCJLrbl.o(.text+0x20f): In function `main':
: undefined reference to `Stack<int>::~Stack [in-charge]()'
/tmp/ccCJLrbl.o(.text+0x229): In function `main':
: undefined reference to `Stack<int>::~Stack [in-charge]()'
collect2: ld returned 1 exit status


I'm tearing my hair out... why would it not work in seperate files?
My class definition is something like this:

template <class T>
class Stack
{
private:
StackNode<T> *stackHead;
/// The number of elements on the stack. It's easier to keep count
/// of them here than to count them every time we need them.
unsigned int stackSize;
protected:
public:
/// Default constructor: set the stackSize to be 0.
Stack(void);

/// Default destructor: clean up any memory allocated.
~Stack(void);
/// push: push an item onto the stack, and incrememt the size.
bool push(T item);

/// pop: return the topmost element from the stack, removing it
from the stack at the same time:
T pop(void);

/// size: returns how many items are on the stack:
unsigned int size(void) const;
/// empty: return true if the stack is empty:
bool empty(void) const;
/// full: return true if the stack is full: - for a linked list
implementation, this can be safely ignored:
bool full(void) const;

};


and the code for a single method for the above class looks like this:

template <class T> bool Stack<T>::push(T item)
{
if (full())
return false;

StackNode<T> *node = new StackNode<T> (item);
node->Head = stackHead;
stackHead = node;
stackSize++;

return true;
}

Since you didn't tell us, I'll suppose that
you moved the function template definitions
from your stackList.h header file
to your stackList.cpp source file and that
you include'd the stackList.h header file
but *not* the stackList.cpp source file
in your test.cpp source file.

Please allow me to ask a simple question,

"How does the C++ compiler know
where to find the function template definitions
so that it can instantiate them
when it is compiling test.cpp?"
I've been following the C++ language tutorial,
and a few books I own at home.
info gcc 'C++ extensions' 'Template Instantiation'
It looks like I'm doing everything properly...
Can anyone shed some light on this?

If you know all of the template functions that you need.
you can instantiate the template functions *explicitly*:

template Stack<int>::Stack(void);
template std::bool Stack<int>::push(int);
template size_t Stack<int>::size(void) const;
template std::bool Stack<int>::empty(void) const;
template int Stack<int>::pop(void);
template void Stack<int>::~Stack(void);

right after the function template definitions
in your stackList.cpp source file.
 
D

David Hilsee

Thomi Richards said:
Hi,


I'm trying to create a simple stack class using C++ and templates.
Everything works well and good if I amke the class totally inline.
However, as soon as I try to seperate the class into a .h and a .cpp
file, gcc throws some strange linking errors.
[...]
I'm tearing my hair out... why would it not work in seperate files? My
class definition is something like this:
[...]

See the FAQ (http://www.parashift.com/c++-faq-lite/), section 34 ("Container
classes and templates"), question 12 ("Why can't I separate the definition
of my templates class from it's declaration and put it inside a .cpp file?")
and the related questions that follow.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top