Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem Linking "static" Methods in C++

Tags:

c++

g++

linker

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.

like image 598
Srikanth Avatar asked Sep 22 '08 18:09

Srikanth


People also ask

What's wrong with static methods?

Static methods are bad for testability. Since static methods belong to the class and not a particular instance, mocking them becomes difficult and dangerous. Overriding a static method is not that simple for some languages.

Is static data member can be accessed using this pointer?

A static member function can be called, even when a class is not instantiated. A static member function cannot have access to the this pointer of the class.

Can static methods modify state?

static means that the method is bound to the object definition (the class) and not to any specific object instance. Thus static method cannot change the state of it's own object as it simply does not have an instance to work on (it does not have a this ).

What is a non-static member function?

A non-static member function is a function that is declared in a member specification of a class without a static or friend specifier. ( see static member functions and friend declaration for the effect of those keywords)


2 Answers

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)

like image 129
8 revs Avatar answered Oct 01 '22 06:10

8 revs


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...

like image 37
Herms Avatar answered Oct 01 '22 05:10

Herms