Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Calling a non-static member function as a thread function

2.90/5 (6 votes)
27 Oct 20064 min read 1   642  
This article describes how to call a C++ class member function as a thread function.

Introduction

When you want to define a thread function to access the methods and properties of a class, the common way is to define a static member function, pass the address of that function to the CreateThread() function together with a pointer to a class instance (this), and use it inside the static member function to access the methods and properties of that instance:

//
// A Base class
//
class Base
{
public:
   Base(){}
   virtual ~Base(){}
   virtual VOID MemberFunc(UINT i) // A member function
   { fprintf(stderr, "Base::MemberFunc(0x%x)\n", i); }
   static UINT ThreadFunc(LPVOID param)
   {
      Base* This = (Base*)param;
      This->MemberFunc(0); // call a member function
   }
   VOID StartThreadFunc()
   {
      ::CreateThread(0,0,(LPTHREAD_START_ROUTINE)ThreadFunc,
                    (LPVOID)this,0,0);
   }
};
In this article, I am going to present a new way to call a non-static member function.

Using the code

I have provided a header and a CPP file (named krunner.h and krunner.cpp) for the code of the base class which handles calling the non-static member function. You need to include these two files in your project. The header file has the following base class definition in it. The class has a pure virtual function named Run() which will be your thread function and which you need to implement in your class derived from CRunnable.
#define CallType __stdcall
//
// Run() function will be the thread function
//
class CRunnable
{
public:
   virtual UINT CallType Run() = 0;
   virtual UINT CallType Start();
   virtual UINT CallType Terminate();
   CRunnable();
   virtual ~CRunnable();
};

In your code, you need to include the header file and define your own class derived from CRunnable.

#include "krunner.h"
// include the header file in your code

// A sample base class
class Base
{
public:
   Base(){}
   virtual ~Base(){}
   virtual VOID MemberFunc(UINT i) // A member function
   { fprintf(stderr, "Base::MemberFunc(0x%x)\n", i); }
};

// A sample derived class
class Derived : public CRunnable, Base
// CRunnable must be the first
{
public:
   // implement CRunnable
   virtual UINT CallType Run()
   {
      // your thread code goes here
   }
   virtual UINT CallType Start()
   {
      UINT ret = CRunnable::Start(); // should be called first
      // add your own start code here
      return ret;
   }
   virtual UINT CallType Terminate()
   {
      // add your own terminate here
      return CRunnable::Terminate(); // should be called last
   }
};

// A base class modified so as to derive from CRunnable
class ModifiedBase : public CRunnable
{
public:
   ModifiedBase(){}
   virtual ~ModifiedBase(){}
   virtual VOID MemberFunc(UINT i) // A member function
   { fprintf(stderr, "ModifiedBase::MemberFunc(0x%x)\n", i); }

   // implement CRunnable
   virtual UINT CallType Run()
   {
      // your thread code goes here
   }
   virtual UINT CallType Start()
   {
      UINT ret = CRunnable::Start(); // should be called first
      // add your own start code here
      return ret;
   }
   virtual UINT CallType Terminate()
   {
      // add your own terminate here
      return CRunnable::Terminate();
      // should be called last
   }
};

Then, what is required is to define an instance of your derived class, and call the Start() and Terminate() member functions.

int main(int argc, char** argv)
{
   Derived obj;

   obj.Start();

   // do something
   obj.CallMemberFunc();

   obj.Terminate();

   return 0;
}
or define an instance of your ModifiedBase class and call the Start() and Terminate() member functions.
int main(int argc, char** argv)
{
   ModifiedBase obj;

   obj.Start();

   // do something
   obj.CallMemberFunc();

   obj.Terminate();

   return 0;
}

The following is the output of the sample application I have provided in the Zip file:

main -- starting...
Derived::Start -- waiting...
Derived::Run-0x90c -- started
Derived::CallMemberFunc-0x250 -- PostThreadMessage(0x90c, 
                                 TM__CALLMEMBERFUNC, 0x250, 0)
Derived::Run-0x90c -- TM__CALLMEMBERFUNC
Base::MemberFunc(0x250)
main -- terminating...
Derived::Terminate-0x250 -- PostThreadMessage(0x90c, 
                            TM__EXIT, 0, 0)
Derived::Run-0x90c -- TM__EXIT
Derived::Run-0x90c -- ending
Derived::Terminate-0x250 -- call CRunnable::Terminate()
main -- terminated

Remarks

  • My code assumes the Run() function in the virtual function table is the first one and should be so in a derived class as well. If you have a derived class and have already derived from a base class and would like to include my base class, you need to use multiple inheritance and put my class (CRunnable) in the first place in the list of base classes. On the other hand, you can modify your base class so as to derive from my class (CRunnable) to avoid multiple inheritance. In that case, you need to add the virtual functions of my class (CRunnable) into your base class. In the sample application, I have used multiple inheritance.
  • You need to handle the synchronisation and communication issues among your threads in your derived classes. You can see an example of how it is done in my sample code.
  • CreateThread() is called with three NULL parameters: CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)ThreadFunc,this,NULL,&Dummy).
  • You need to link with the multi-threaded version of the CRT library. Go to Project Settings, C/C++ tab, Use run-time library must be Multithreaded or Multithreaded DLL for your release builds.
  • There is a warning about the memory leaks when you use CRT functions in your thread functions started by calling the CreateThread function, and you are usually warned of using _beginthreadex instead. My code has been coded to call CreateThread, and it has to be that way. However, there is still a workaround to cope with the memory leak issues even if you do it.

    You may wonder why your Win32-based applications seemed to work over the years even though you've been calling CreateThread instead of _beginthreadex. When a thread calls a CRT function that requires the tiddata structure (which is usually allocated and initialized by _beginthreadex), here is what happens. First, the CRT function attempts to get the address of the thread's data block (by calling TlsGetValue). Second, if NULL is returned as the address of the tiddata block, then the calling thread doesn't have a tiddata block associated with it. At this point, the CRT function allocates and initializes a tiddata block for the calling thread right on the spot. The block is then associated with the thread (via TlsSetValue), and this block will stay with the thread for as long as the thread continues to run. Third, the CRT function can now use the thread's tiddata block, and so can any CRT function that is called in the future. This, of course, is fantastic because your thread runs without a hitch (almost). Well, actually there are a few problems here. If the thread uses the CRT's signal function, the entire process will terminate because the structured exception handling frame has not been prepared. Also, if the thread terminates without calling _endthreadex, the data block cannot be destroyed and a memory leak occurs. So, if you would call _endthreadex for a thread created with CreateThread, you should be OK.

History

  • 27 Oct 2006 - First release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here