tags:

views:

4043

answers:

6

I want to call a few "static" methods of a CPP class defined in a different file but I'm having linking problems. I created a test-case that recreates my problem and the code for it is below.

(I'm completely new to C++, I come from a Java background and I'm a little familiar with C.)

// CppClass.cpp
#include <iostream>
#include <pthread.h>

static pthread_t thread;
static pthread_mutex_t mutex;
static pthread_cond_t cond;
static int shutdown;

using namespace std;

class CppClass
{
public:
        static void Start()
        {
                cout << "Testing start function." << endl;
                shutdown = 0;
                pthread_attr_t attr;
                pthread_attr_init(&attr);
                pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
                pthread_mutex_init(&mutex, NULL);
                pthread_cond_init(&cond, NULL);

                pthread_create(&thread, &attr, run_thread, NULL);
        }

        static void Stop()
        {
                pthread_mutex_lock(&mutex);
                shutdown = 1;
                pthread_cond_broadcast(&cond);
                pthread_mutex_unlock(&mutex);
        }

        static void Join()
        {
                pthread_join(thread, NULL);
        }
private:
        static void *run_thread(void *pthread_args)
        {
                CppClass *obj = new CppClass();
                pthread_mutex_lock(&mutex);
                while (shutdown == 0)
                {
                        struct timespec ts;
                        ts.tv_sec = time(NULL) + 3;
                        pthread_cond_timedwait(&cond, &mutex, &ts);
                        if (shutdown)
                        {
                                break;
                        }
                        obj->display();
                }
                pthread_mutex_unlock(&mutex);
                pthread_mutex_destroy(&mutex);
                pthread_cond_destroy(&cond);
                pthread_exit(NULL);
                return NULL;
        }

        void display()
        {
                cout << " Inside display() " << endl;
        }
};

// main.cpp
#include <iostream>
/* 
 * If I remove the comment below and delete the
 * the class declaration part, it works.
 */
// #include "CppClass.cpp"
using namespace std;

class CppClass
{
public:
        static void Start();
        static void Stop();
        static void Join();
};

int main()
{
        CppClass::Start();
        while (1)
        {
                int quit;
                cout << "Do you want to end?: (0 = stay, 1 = quit) ";
                cin >> quit;
                cout << "Input: " << quit << endl;
                if (quit)
                {
                        CppClass::Stop();
                        cout << "Joining CppClass..." << endl;
                        CppClass::Join();
                        break;
                }
        }
}

When I tried to compile, I get the following error:

$ g++ -o go main.cpp CppClass.cpp -l pthread
/tmp/cclhBttM.o(.text+0x119): In function `main':
: undefined reference to `CppClass::Start()'
/tmp/cclhBttM.o(.text+0x182): In function `main':
: undefined reference to `CppClass::Stop()'
/tmp/cclhBttM.o(.text+0x1ad): In function `main':
: undefined reference to `CppClass::Join()'
collect2: ld returned 1 exit status

But if I remove the class declaration in main.cpp and replace it with #include "CppClass.cpp", it works fine. Basically, I want to put these declarations in a separate .h file and use it. Am I missing something?

Thanks for the help.

A: 

Can you put the C++ class code in your question in a code block? It's unreadable as is.

edit: thanks, much nicer.

Colen
+2  A: 

I think you want to do something like:

g++ -c CppClass.cpp g++ -c main.cpp g++ -o go main.o CppClass.o

That should resolve it.

Jon
+1  A: 

make a .h file with the class definition in it, and then #include that file into your 2 files.

Greg Rogers
A: 

Sure looks like the linker is not picking up you second source file.

Benoit
+22  A: 

It's obvious you come from a Java background because you haven't yet grasped the concept of header files. In Java the process of defining something is usually in one piece. You declare and define at the same time. In C/C++ it's a two-step process. Declaring something tells the compiler "something exists with this type, but I'll tell you later how it is actually implemented". Defining something is giving the compiler the actual implementation part. Header files are used mostly for declarations, .cpp files for definitions.

Header files are there to describe the "API" of classes, but not their actual code. It is possible to include code in the header, that's called header-inlining. You have inlined everything in CppClass.cpp (not good, header-inlining should be the exception), and then you declare your class in main.cpp AGAIN which is a double declaration in C++. The inlining in the class body leads to code reduplication everytime you use a method (this only sounds insane. See the C++ faq section on inlining for details.)

Including the double declaration in your code gives you a compiler error. Leaving the class code out compiles but gives you a linker error because now you only have the header-like class declaration in main.cpp. The linker sees no code that implements your class methods, that's why the errors appear. Different to Java, the C++ linker will NOT automatically search for object files it wants to use. If you use class XYZ and don't give it object code for XYZ, it will simply fail.

Please have a look at Wikipedia's header file article and Header File Include Patterns (the link is also at the bottom of the Wikipedia article and contains more examples)

In short:

For each class, generate a NewClass.h and NewClass.cpp file.

In the NewClass.h file, write:

class NewClass {
public:
   NewClass();
   int methodA();
   int methodB();
}; <- don't forget the semicolon

In the NewClass.cpp file, write:

#include "NewClass.h"

NewClass::NewClass() {
  // constructor goes here
}

int NewClass::methodA() {
  // methodA goes here
  return 0;
}

int NewClass::methodB() {
  // methodB goes here
  return 1;
}

In main.cpp, write:

#include "NewClass.h"

int main() {
  NewClass nc;
  // do something with nc
}

To link it all together, do a

g++ -o NewClassExe NewClass.cpp main.cpp

(just an example with gcc)

Thorsten79
@Srikanth: you should mark this one the answer.
Ben Collins
Thanks to all.@Ben: I did that.
artknish
+6  A: 

You're defining the class twice, which I'm pretty sure doesn't work.

Try something like this:

First a header CppClass.h file:

// CppClass.h
using namespace std;

class CppClass
{
public:
    static void Start();
    static void Stop();
    static void Join();
private:
    void *run_thread(void *pthread_args);
    void display();
};

Then a CppClass.cpp file implementing it:

// CppClass.cpp
#include <iostream>
#include <pthread.h>
#include "CppClass.h"

using namespace std;

void CppClass::Start()
{
    /* method body goes here */
}
void CppClass::Stop()
{
    /* method body goes here */
}
void CppClass::Join()
{
    /* method body goes here */
}
void *CppClass::run_thread(void *pthread_args)
{
    /* method body goes here */
}
void CppClass::display() {
    /* method body goes here */
}

Then your main file:

// main.cpp
#include "CppClass.h"

int main()
{
    /* main method body here */
}

I believe the g++ call would be the same.

Basically, you can't declare the same class twice. You should declare the class in the header file, then declare the implementation in the cpp file. You could also put all the code inline in a single declaration of the class in a header file. But declaring it twice like you did won't work.

I hope that made sense...

Herms