0

I'm working on a program right now and to test template classes (which I will need) I wrote a small (and buggy, chances are 2 or 3 logic bugs in it, my goal is to get it to compile) stack class. What I want to do is to compile it to a static library (.a) then link it with the main program.

The error is:

main.cpp:(.text+0x1c): undefined reference to `Stack<int>::Stack()'
main.cpp:(.text+0x31): undefined reference to `Stack<int>::push(int)'
main.cpp:(.text+0x42): undefined reference to `Stack<int>::push(int)'
main.cpp:(.text+0x4e): undefined reference to `Stack<int>::pop()'
main.cpp:(.text+0x5d): undefined reference to `Stack<int>::pop()'
collect2: error: ld returned 1 exit status

This is the header file:

/* stack.h */
#ifndef _STACK_INCLUDED_
#define _STACK_INCLUDED_
template<typename T>
struct Node
{
    T* node;
    Node<T>* next;
};

template<typename T>
class Stack
{
private:
    Node<T>* bottom;
public:
    Stack();
    Stack(T first);
    Stack(T* arr, int amount);
    void push(T push);
    T* pop();
};
/* I added the following prototypes in an attempt to correct the error,
did not work*/
template<typename T>
Stack<T>::Stack();

template<typename T>
Stack<T>::Stack(T first);

template<typename T>
Stack<T>::Stack(T* arr, int amount);

template<typename T>
void Stack<T>::push(T push);

template<typename T>
T* Stack<T>::pop();

#endif

Here is the implementation file:

/* stack.cpp */
#include "../heads/stack.h"
#define null (void*)0

template<typename T>
Stack<T>::Stack() {
    bottom = null;
}
template<typename T>
Stack<T>::Stack(T first) {
    push(first);
}
template<typename T>
Stack<T>::Stack(T* arr, int amount)
{
    int i;
    for(i=0;i<amount; i++)
    {
        push(arr[i]);
    }
}
template<typename T>
void Stack<T>::push(T push)
{
    Node<T>* tmp = new Node<T>();
    tmp->node = push;
    tmp->next = null;

    Node<T>* node = bottom;
    while(node->next != null)
    {
        node = node->next;
    }
    node->next = tmp;
}
template<typename T>
T* Stack<T>::pop()
{
    int i=0;
    Node<T>* node = bottom;
    while(node->next != null)
    {
        i++;
        node = node->next;
    }
    node = bottom;
    for(;i>1;i++)
    {
        node = node->next;
    }
    Node<T>* res = node->next;
    node->next = null;
    return res->node;
}

You might have noticed the header file is included: "../heads/stack.h", this is because the structure looks like so:

- root
-- CLASSNAME
--- implementation of CLASSNAME
-- heads
--- all the headers
-- obj
--- object files (compiled)
--bin
---final output

.

The makefile looks like so:

CC=g++
CFLAGS=-c -fpermissive -Wall
LFLAGS=-llua
OBJ=obj
BIN=bin
HS=heads

all: $(OBJ)/bind.a $(OBJ)/stack.a $(OBJ)/main.o
    $(CC) $(LFLAGS) -o $(BIN)/main $(OBJ)/main.o $(OBJ)/bind.a $(OBJ)/stack.a
$(OBJ)/bind.o: binds/bind.cpp $(HS)/bind.h
    $(CC) $(CFLAGS) binds/bind.cpp -o $(OBJ)/bind.o
$(OBJ)/bind.a: $(OBJ)/bind.o
    ar -cvq $(OBJ)/bind.a $(OBJ)/bind.o
$(OBJ)/main.o:
    $(CC) $(CFLAGS) main/main.cpp -o $(OBJ)/main.o
$(OBJ)/stack.o: $(HS)/stack.h stack/stack.cpp
    $(CC) $(CFLAGS) stack/stack.cpp -o $(OBJ)/stack.o
$(OBJ)/stack.a: $(OBJ)/stack.o
    ar -cvq $(OBJ)/stack.a $(OBJ)/stack.o
clean:
    touch $(OBJ)/dummy
    rm $(OBJ)/*

Compiler: g++ 4.7.2 (gcc-multilib)
OS: Arch Linux (x86_64) (kernel 3.6.6-1)

You can get the whole file here (I'm doing multiple test, so don't mind the -llua flag and other files, you can change the Makefile if needed, I just want to figure this out).

After some research and testing, I noticed that the symbols aren't being exported, (nm obj/stack.o shows nothing, while nm obj/bind.o does. same thing for (.a)).
However I could not find any reference to what to do in this case.

1 Answers1

0

You can't make a lib for every possible template parameters, but you can for some types.

A templated class is used to generate code of a class that taking the given type.

When there is a instance using it, it will generate the actual class, otherwise, it will be simply omitted when compile.

According to Compile header-only template library into a shared library? this is possible by using Explicit Instantiation, and make the types you declared into the lib, and let the rest to generate in compile.

Community
  • 1
  • 1
xiaoyi
  • 6,641
  • 1
  • 34
  • 51
  • if you try to avoid compiling to library (in the makefile, replace in all: `$(OBJ)/stack.a` with `$(OBJ)/stack.h`. This still wouldn't work. –  Nov 20 '12 at 16:35
  • @Shingetsu, you have to provide the implementation code at the same time. – xiaoyi Nov 20 '12 at 16:37
  • thank you, got it working by adding `template class Stack;` at the end of implementation file (.cpp) –  Nov 20 '12 at 17:58
  • @Shingetsu I did some study yesterday. It's probably better to put the implementation in a `.h-inl` which is included in the corresponding header `.h`. And in the `.cc` include `.h`, and put explicit instantiation after the including. Which will gurantee the code when using other template arguments – xiaoyi Nov 21 '12 at 16:40