1

For some reasons, sometimes when I define my member functions in a CPP file rather than within their declaring header, I get undefined reference errors from g++. This question is similar to Undefined Reference To Member function but that user managed to solve the problem by adding the missing file to his command line, which doesn't seem to be working here. Btw, I'm not against using a makefile; in fact I plan to do so eventually, whenever I get used to these commands.

my main file ('ringbuftest.cpp'):

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
#include <cstring>
#include "ringbuffer.h"
using namespace std;

void *reader(void* param);
void *writer(void* param);

struct pack{
  char msg[256];
};

RINGBUFFER<pack> *ringo;

int main(){
pthread_t rdr;
pthread_t wtr;

ringo = new RINGBUFFER<pack>(12);

int ret1 = pthread_create(&rdr,NULL,reader,(void*)NULL);
int ret2 = pthread_create(&wtr,NULL,writer,(void*)NULL);

#ifdef _unix
cout<< "unix single underscore\n";
#elif defined __unix
cout<< "unix two underscores\n";
#endif

pthread_join(wtr,NULL);
pthread_join(rdr,NULL);

cout<< "threads are done\n";
exit(0);
return 0;
}

void *reader(void *param){
  pack myPack;
  while(true)
  {
    for(int i=0;i<10000;i++){int k=0;k=i;k++;i=k;i--;}
    if( ringo->Pop(&myPack) )
    {
      cout<< myPack.msg;
    }
  }
}
void *writer(void *param){
  pack myPack;
  while(true){
    strcpy(myPack.msg,"hello reader!\n");
    ringo->Push(&myPack);
  }
}

my class header ('ringbuffer.h'):

#include<stdlib.h>
using namespace std;

#ifndef __ring_buffer_h__
#define __ring_buffer_h__

#define RINGBUFFER_DEFAULT_SIZE 8

template <class T>
class RINGBUFFER
{
private:
    unsigned int top;
    unsigned int bottom;
    unsigned int size;
    unsigned int count;
    void *items;
public:
    RINGBUFFER();
    RINGBUFFER(unsigned int size);
    ~RINGBUFFER();
    bool Push(T *value);
    bool Pop(T *value);
};


#endif

my class definitions ('ringbuffer.CPP'):

#include "ringbuffer.h"

template<class T>
RINGBUFFER<T>::RINGBUFFER()
{
    top = bottom = 0;
    size = RINGBUFFER_DEFAULT_SIZE;
    count = 0;
    items = malloc((size+1)*sizeof(T));
}

template<class T>
RINGBUFFER<T>::RINGBUFFER(unsigned int _size)
{
    top = bottom = 0;
    size = _size;
    count = 0;
    items = malloc(size*sizeof(T));
}

template<class T>
RINGBUFFER<T>::~RINGBUFFER()
{
    free(items);
}

template<class T>
bool RINGBUFFER<T>::Push(T *value)
{
    if( count<size )
    {
        memcpy(&(((T*)items)[bottom]),value,sizeof(T));
        bottom = (bottom+1)%size;
        count++;
        return true;
    }
    return false;
}

template<class T>
bool RINGBUFFER<T>::Pop(T *value)
{
    if( count>0 ) 
    {
        memcpy(value,&(((T*)items)[top]),sizeof(T));
        top = (top+1)%size;
        count--;
        return true;
    }
    return false;
}

To compile, I've been trying to use:

g++ ringbuffer.CPP ringbuftest.cpp -lpthread -o ringbuffertest.o

and I get the errors:

/tmp/ccj8RqhY.o: In function `main':
ringbuftest.cpp:(.text+0x21): undefined reference to `RINGBUFFER<pack>::RINGBUFFER(unsigned int)'
/tmp/ccj8RqhY.o: In function `reader(void*)':
ringbuftest.cpp:(.text+0x157): undefined reference to `RINGBUFFER<pack>::Pop(pack*)'
/tmp/ccj8RqhY.o: In function `writer(void*)':
ringbuftest.cpp:(.text+0x1db): undefined reference to `RINGBUFFER<pack>::Push(pack*)'
collect2: error: ld returned 1 exit status

I'm guessing I'm doing something wrong with g++ because I keep hitting this problem in different projects, or maybe I'm using templates wrong.(?) How can I resolve this error?

Edit: I should mention that this compiles perfectly if I instead paste the member definitions into the header file and exclude the .CPP from the g++ command.

Community
  • 1
  • 1
  • possible duplicate of [C++ undefined reference to template class method](http://stackoverflow.com/questions/14914129/c-undefined-reference-to-template-class-method) – Mat Mar 02 '13 at 15:23
  • 1
    Also don't use `-lpthread`. Use `-pthread` instead. (Unless you have a very good reason not to.) – Mat Mar 02 '13 at 15:24
  • Do not put double underscores in your include guards, all names with double underscores are reserved for the implementation. Do not put `using namespace` in a header. Your include guards should be at the top of the file, not after the `#include` and the very naughty `using`. Finally, naming a class template in ALL_CAPS is weird. – Jonathan Wakely Mar 04 '13 at 21:27

1 Answers1

1

You need to move the entire definition of RINGBUFFER<T> from ringbuffer.cpp to ringbuffer.h so that it's visible when ringbuftest.cpp is being compiled.

NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • is this specific to template classes? I had this same error on a non-template class until I rearranged the order of files on the compile command, into order of dependency. – user2126895 Mar 02 '13 at 15:29