Click here to Skip to main content
Click here to Skip to main content

Encapsulating Win32 threads in C++

, 26 Jul 2001
Rate this:
Please Sign up or sign in to vote.
This article presents a class to encapsulate threads, leaving the user to focus on project details.
<!-- Download Links -->

Intent

Encapsulating Win32 threads in a C++ class, easy to subclass and reuse. Hide the details of threads from users so they can focus on the project details.

Motivation

Object oriented languages like C++ have their strength in their ability to encapsulate the representation and implementation of an object, so that programming is focused on a higher level. We say we're programming at interface level rather than at function level. However most OSes haven't been design with C++ in mind, they were usually implemented using non-OO approaches. That's why sometimes it can be tricky to encapsulate some platform dependent resources, like threads for instance. My approach covers Win32 threads.

Win32 threads

To create another thread in the same process, one has in Win32 couple of API functions that handle threads. However they are C and not C++ API. We can easily notice C idioms like callback functions, conversions to and from void* to other types and so on. Let's take a look at how CreateThread, the API function that creates a thread in Win32 looks like. It's prototype it's shown below:

	HANDLE CreateThread(
		LPSECURITY_ATTRIBUTES lpThreadAttributes, 
		DWORD dwStackSize, 
		LPTHREAD_START_ROUTINE lpStartAddress, 
		LPVOID lpParameter, 
		DWORD dwCreationFlags, 
		LPDWORD lpThreadId
	);

lpStartAddress is a pointer to a callback function that will run in the new thread, and lpParameter is a parameter of type void* passed to the new thread. Passing a pointer to a callback function however it's not in the spirit of OOP, and it comes like a serious impediment if we want to encapsulate threads in classes. The callback function required to be passed to CreateThread looks like this:

	DWORD WINAPI ThreadProc(
		LPVOID lpParameter
	);

We'll notice this prototype will keep us from having this callback function as a member of a class, as it's member functions are passed a hidden parameter: this. What's to be done then? Did we fail miserably? Not yet. We cannot use member function and we've seen why, however classes have static methods, which have single instances for all objects. They are connected to classes rather than to objects. That's why they are not passed this as a parameter. So a static function becomes an interesting candidate for a callback function. However there's one small problem, if we have our thread in a static method, then no matter how many objects of that class we'll have, there will be only one thread, as a static method has a single instance per class. This is not what we want. We want our working thread to be a member method, easy to override by subclasses, and all this workaround to be transparent for the clients. Can we do that? Yes we can. If you look at ThreadProc, you'll notice it can be passed one void* parameter. Nothing prevents us from sending it (void*)this, and in ThreadProc we just call our working method, now that we have this pointer. The code for doing that will look like:

//here we create the thread
HANDLE CThread::CreateThread ()
{
	return ::CreateThread ((NULL, 0, (unsigned long (__stdcall *)(void *))this->runProcess, 
				(void *)this, 0, NULL);
}	

//static method
int CThread::runProcess (void* pThis)
{
	return ((CThread*)(pThis))->Process();
}

//our working method, virtual, overridable
int CThread::Process ()
{
	//will work in another thread
}

So far so good. We managed to provide an encapsulation of threading mechanism, so users will simply have to implement their own Process, and then call CreateThread member function. It's even easier to provide reusability, as a user can simply inherit from our class defined above, CThread, and simply implement Process, and then call CreateThread, and they have a thread simple as that. However there's a small issue to note here: Let's say we have a subclass of CThread, named CMyThread. In CreateThread will convert this (which is of type CMyThread*) to void* and pass it to runProcess, where we reconvert this to CThread. C++ standard states that if you convert a type X* to void*, then only a conversion back, to the same type X* is permitted. Other conversions result in an undefined behaviour. That simply means we've done something wrong. How can we fix that? Well, with a small workaround.

struct workAround {
	CThread* this_thread;
};

//we pass a workAround struct instead of this
HANDLE CThread::CreateThread ()
{
	workAround* wA	= new workAround;
	wa->this_thread	= this;
	return ::CreateThread ((NULL, 0, (unsigned long (__stdcall *)(void *))this->runProcess, 
				(void *)wa, 0, NULL);
}	

//static method
int CThread::runProcess (void* pThis)
{
	workAround* wA	= (workAround*)pThis;
	//this will call the appropriate method, as Process is a virtual method
	CThread* thread = wA->this_thread;
	delete wA;
	return thread->Process();
}

This time we're allright as we're converting to and from the same type (struct workAround). Finally to be in the spirit of C++ we'll use C++ conversion, instead of C conversions. Eg: instead of (void*)wA we'll have static_cast<void*>(wA)

Conclusions

Due to the fact most nowadays OSes are not object oriented, as a C++ we usually have to find workarounds when we need to encapsulate platform dependent resources. We have to apply different tricks to achieve that, but once we encapsulated it, it's very simple to use and reusable with considerable less effort. Threads in Win32 are a good examples in that direction. You can further study the source code to get a deeper insight. Happy programming!

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

Share

About the Author

Ciprian Miclaus

United States United States
No Biography provided

Comments and Discussions

 
Questiontype conversion Pinmember[C]L0NE29-Nov-11 4:41 
QuestionType mismatch while calling CreateThread() PinmemberAhmed Han24-Dec-09 1:10 
QuestionBorland IDE error Pinmemberloneaussie1-Aug-07 6:41 
GeneralMessage Box disappeared Pinmembersudiptam1-May-05 23:58 
Generalundefined behaviour PinsussAnonymous11-May-04 23:29 
Generalwrong virtual function called Pinmemberchrismc91225-Jan-04 21:59 
GeneralMemory leak if CreateThread gives an error Pinmemberyogieric5-Dec-03 10:35 
Generalparameter Pinsussmc_fly24-Dec-02 2:55 
if i had a socket in my main part of the program and i called a thread like this:
 
CMyThread cmt;
cmt.CreateThread();
(like in the demo project)
 
how would i pass the socket as a parameter to the thread so it can use it?
Questionwhat would happen ... Pinmemberinner2-Dec-02 10:01 
Generalno need for static member function PinmemberSilvio Iaccarino31-Jul-01 2:17 
GeneralRe: no need for static member function PinmemberTomasz Sowinski31-Jul-01 3:20 
GeneralRe: no need for static member function PinmemberAnonymous31-Jul-01 3:38 
GeneralRe: no need for static member function Pinmembersilvio@iaccarino.de31-Jul-01 14:02 
GeneralCreateThread requires an ID pointer in Win9x PinmemberLee Lorenz28-Jul-01 14:12 
GeneralUse _beginthreadex PinmemberDaniel Kopitchinski28-Jul-01 0:35 
GeneralRe: Use _beginthreadex PinmemberLee Lorenz28-Jul-01 14:09 
GeneralRe: Use _beginthreadex PinmemberMichael Dunn28-Jul-01 15:25 
GeneralRe: Use _beginthreadex PinmemberLee Lorenz29-Jul-01 6:29 
GeneralRe: Use _beginthreadex PinmemberDaniel Lohmann29-Jul-01 10:39 
GeneralRe: Use _beginthreadex - EXCEPT... when using MFC PinmemberLee Lorenz29-Jul-01 13:10 
GeneralRe: Use _beginthreadex - EXCEPT... when using MFC PinmemberMichael Dunn29-Jul-01 14:48 
GeneralGood start, but needs some work PinmemberJason Douglas27-Jul-01 2:44 
GeneralRe: Good start, but needs some work PinmemberZoltan Csizmadia27-Jul-01 4:06 
GeneralRe: Good start, but needs some work PinmemberJason Douglas27-Jul-01 11:26 
GeneralRe: Good start, but needs some work PinmemberZoltan Csizmadia27-Jul-01 12:14 
GeneralRe: Good start, but needs some work PinmemberPaolo Messina27-Jul-01 13:42 
GeneralRe: Good start, but needs some work PinsussPtadeep20-Mar-03 18:01 
GeneralRe: Good start, but needs some work Pinmemberconcombre0018-Apr-06 5:16 
GeneralRe: Good start, but needs some work PinmemberPony27922-Oct-12 20:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140827.1 | Last Updated 27 Jul 2001
Article Copyright 2001 by Ciprian Miclaus
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid