It’s a frequently asked question in C++ forums and newsgroups. Try to create a thread and pass an instance member function of a C++ class as the thread start routine and the compiler balks. What is the compiler complaining about? How can a thread be run within an instance of a class?
The Microsoft C runtime library (MSCRT) includes the _beginthreadex1 routine.
uintptr_t _beginthreadex(
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
_beginthreadex expects a pointer to a function, a function that will be executed by the newly created thread. It’s the start_address parameter in the function definition above.
If a non-static member function of a class is passed to the _beginthreadex function as the start_address parameter, the code will fail to compile. (With the Microsoft compiler the error might be one of the following depending on the specific syntax used: C3867, C2276, or C2664.)
Members of a C++ class can be static or non-static. Static members are sometimes known as class members. Non-static members are sometimes known as instance members. Instance member functions operate on a specific instance of a class. They are able to do so because the C++ compiler provides instance member functions with a ‘this’ pointer as a hidden function argument. The hidden extra argument is the source of the compiler’s consternation. An instance member function can’t be passed as the start_address parameter.
Static member function don’t have the special ‘this’ pointer magic. Can a static member function be passed as the start_address? Yes. But if the goal is to create a class whose instances are separate threads, passing a static member function would seem at first to not be much of an advance towards a solution.
As a student I was told that static member functions can not access non-static data members or call non-static member functions. It’s a simplistic statement that implies a prohibition that doesn’t exist. Statics have no knowledge of any specific instances of a class. There’s no ‘this’ pointer in a static member function. But static functions have the same permissions on the class as non-static functions. In other words if a static function is given a ‘this’ pointer it can use the ‘this’ pointer to access the data and functions of a specific instance.
All that needs to be done is to arrange to pass a ‘this’ pointer to the static member function. _beginthreadex has an arglist parameter for passing arguments to the thread start routine. Below is example code that implements this technique.
A couple of things to note:
- Data that the thread needs to operate on can be stored in the object instance. Only the ‘this’ pointer needs to be passed via the thread start routine.
- The general technique of using a static member function to forward to a specific object instance can be applied to encapsulating any C language style callback.
Windows thread class
This code was written and tested with Visual Studio 2005.
#define WIN32_LEAN_AND_MEAN
#include <process.h>
#include <windows.h>
#include <iostream>
class thread
{
public:
thread();
virtual ~thread();
const HANDLE& GetHandle() const { return m_hThread; }
const bool wait() const;
private:
// copy operations are private to prevent copying
thread(const thread&);
thread& operator=(const thread&);
static unsigned __stdcall threadProc(void*);
unsigned run();
HANDLE m_hThread;
unsigned m_tid;
};
thread::thread()
{
m_hThread = reinterpret_cast<HANDLE>(
::_beginthreadex(
0, // security
0, // stack size
threadProc, // thread routine
static_cast<void*>(this), // thread arg
0, // initial state flag
&m_tid // thread ID
)
);
if (m_hThread == 0)
{
throw std::exception("failed to create thread");
}
}
thread::~thread()
{
try
{
::CloseHandle(GetHandle());
}
catch(...)
{
// suppress any exception; dtors should never throw
}
}
const bool thread::wait() const
{
bool bWaitSuccess = false;
// a thread waiting on itself will cause a deadlock
if (::GetCurrentThreadId() != m_tid)
{
DWORD nResult = ::WaitForSingleObject(GetHandle(), INFINITE);
// nResult will be WAIT_OBJECT_0 if the thread has terminated;
// other possible results: WAIT_FAILED, WAIT_TIMEOUT,
// or WAIT_ABANDONED
bWaitSuccess = (nResult == WAIT_OBJECT_0);
}
return bWaitSuccess;
}
unsigned __stdcall thread::threadProc(void* a_param)
{
thread* pthread = static_cast<thread*>(a_param);
return pthread->run();
}
unsigned thread::run()
{
std::cout << "Hello from thread" << std::endl;
return 0;
}
int main(int argc, char* const argv[])
{
try
{
std::cout << "Hello" << std::endl;
thread thrd;
thrd.wait();
std::cout << "Done" << std::endl;
}
catch (std::exception& ex)
{
std::cerr << ex.what() << std::endl;
}
catch (...)
{
std::cerr << "unknown exception" << std::endl;
}
return 0;
}
pthreads thread class
Just so you don’t think I’m Windows-centric, below is a pthreads version of the example code. This example was written and tested in Xcode on Mac OS X.
#include <iostream>
#include <exception>
#include <pthread.h>
class thread
{
public:
thread();
virtual ~thread() { }
const pthread_t& getTID() const { return m_tid; }
bool join();
private:
// copy operations are private to prevent copying
thread(const thread&); // no implementation
thread& operator=(const thread&); // no implementation
static void* threadproc(void*);
void* run();
pthread_t m_tid;
};
thread::thread()
{
int nerr = ::pthread_create(
&m_tid,
0,
threadproc,
static_cast<void*>(this)
);
if (nerr)
{
// for brevity a string instead of a custom exception
throw std::string("failed to create thread");
}
}
bool thread::join()
{
// pthread_join will detect attempts to join a thread to itself
int nerr = ::pthread_join(getTID(), 0);
return (nerr == 0);
}
void* thread::threadproc(void* a_param)
{
thread* pthread = static_cast<thread*>(a_param);
return pthread->run();
}
void* thread::run()
{
std::cout << "Hello from thread" << std::endl;
return 0;
}
int main (int argc, char* const argv[])
{
try
{
std::cout << "Hello" << std::endl;
thread thrd;
thrd.join();
std::cout << "Done" << std::endl;
}
catch (std::string& ex)
{
std::cerr << ex << std::endl;
}
catch (std::exception& ex)
{
std::cerr << ex.what() << std::endl;
}
catch (...)
{
std::cerr << "unknown exception" << std::endl;
}
return 0;
}
Related Posts:
Disallowing Copying in a C++ Class
1_beginthreadex is a wrapper that performs some library specific housekeeping and calls the Win32 API’s CreateThread function. If the MS C runtime library is being used, it’s best to use the wrapper and not call CreateThread directly.
Great code for pthreads with g++. Thank you.
Posted by: Doug Smith | July 03, 2008 at 11:58 AM