
I spent the better half of today trying to figure how to assign a C++ class member to a thread in an MFC environment. It’s not as easy as you think. I think the word convoluted comes to mind, but that’s just because I am used to simpler implementations in C, C#, and Python. The purpose of this article is to save you precious time.
Let’s start with a simple MFC application that delegates a simple function to a thread.
#include <iostream>
#include <iafxwin.h>
#include <ivector>
#include <icstdlib>
#include <ictime>
using namespace std;
UINT procrastinate( LPVOID params )
{
int *i = reinterpret_cast(params);
srand( (unsigned int)time(NULL) + *i );
int iSecs = rand() % 5 + 1;
cout << "I will now procrastinate " << iSecs
<< " for seconds" << endl;
Sleep(iSecs * 1000);
return 0;
}
int main( int argc, char **argv )
{
for ( int i = 0; i < 10; i++ )
{
CWinThread *pThread =
AfxBeginThread(procrastinate, reinterpret_cast(&i));
WaitForSingleObject(pThread->m_hThread, INFINITE);
}
return 0;
}
The AfxBeginThread() function is what kicks off the thread. The function that follows AfxBeginThread() is called WaitforSingleObject(). We use it to wait for the thread to complete. If we didn’t use WaitforSingleObject() then the program itself would terminate before the thread(s) could complete. Trust me on this; I wasted about an hour before I figured out what was happening.
Now let’s take a closer look at our example of AfxBeginThread(). The first argument is a function with UINT as the return value and a LPVOID pointer as its parameter. If you don’t know what LPVOID does, then just think of it as a special pointer that can point to anything – this includes functions, classes, structs, integers, dogs, cats—you name it, it can pass it. If you don’t want to pass anything then just set the argument to 0 or NULL. In our case, I am passing the address of the for loop integer. I am using the C++ standard reinterpret_cast operator to make the transition from integer to LPVOID. I don’t know how it works behind the scenes, but just remember you will need to call it in pairs. Once to transform into something else and once to change it back to its original type. We do the latter in our function.
The function itself doesn’t do anything special except generate a random number of seconds to sleep. When we call it in the loop in the main function, it executes then waits to complete. We just want to keep things simple here. Normally, you would want to fire a bunch of threads off at the same time then wait for them to complete; otherwise, what is the point of using threads in the first place. It’s all about concurrency.
Here’s the output of our program.
% ThreddFoxxFunctionOnly.exe
I will now procrastinate 5 seconds
I will now procrastinate 4 seconds
I will now procrastinate 5 seconds
I will now procrastinate 5 seconds
I will now procrastinate 5 seconds
I will now procrastinate 4 seconds
I will now procrastinate 1 seconds
I will now procrastinate 2 seconds
I will now procrastinate 2 seconds
I will now procrastinate 2 seconds
Once you have all the pieces together, running threads with a simple function is rather easy in an MFC application. I encourage you to look at the code and try different options. I am sure that there are many other ways of doing the same thing, maybe even better than our example.
So now we want to do the same thing, but this time let’s call a member of a class. Instinctively, you are probably thinking that this should be an easy thing to do—just pass the object-method as the first argument to AfxBeginThread(). At least, that is what I thought too. Unfortunately, it doesn’t work that way. You have to use some trickery and shenanigans to make this happen, but first some learning.
Functions and class members use different calling conventions under the C++ hood. Functions use __stdcall and class members use __thiscall. AfxBeginThread() expects its functions to use __stdcall. If anything else is used, then your code will not compile. You will soon become angry and frustrated, like I did. Eventually you will abandon C++ for something more appealing, like being a bedpan orderly in a mental hospital—or a patient—if you spend too much time fretting over this quandary.
There is loophole though and our example shows it. First, create a class like you normally would. You then create a static method in the class. This is the method that AfxBeginThread() will call from main. Static methods in classes use __stdcall—that’s the first part of the loophole. The second part is LPVOID. Do you remember that I told you that you can pass anything with it? Well, why not pass an instance of the class itself—that class we created in our example is called ThreddFoxx. The class contains a static method called RunThread(). We will pass ThreddFoxx::RunThread() as the first argument to AfxBeginThread(). Next we will pass as a second argument to AfxBeginThread() a pointer to an ThreddFoxx object that we will instantiate in main just before calling AfxBeginThread() . Remember that we need to use reinterpret_cast to cast the ThreddFoxx object as an LPVOID pointer just like we did in our previous example with the for loop integer. We are almost done. Inside the static method ThreddFoxx::RunThread() definition, we use reinterpret_cast to recast the LPVOID pointer back to the ThreddFoxx object. Now, with the ThreddFoxx object at our disposal, we can call any of its public methods. The one of interest for us is the tellJoke() method. If you are like me, it took me a while to figure this out, but take your time and study the code—you will get it.
Let’s make this example more realistic. We create a simple struct called THREADTRACKER that allows us to bind an instance of a ThreddFoxx along with its corresponding CWinThread handle—which is returned by the AfxBeginThread() function. We then create 10 instances of these structs and stored them in a STL vector. This allows us to keep track of each thread once they have all been spawned. Unlike our previous example of spawning a thread then waiting for it to complete before spawning the next thread, here we spawn all ten at the same time–we then go back and wait for the threads to complete—we then go back again to show you that the values are indeed encapsulated in each unique ThreddFoxx instance. Here’s is the code:
#include <iostream>
#include <iafxwin.h>
#include <ivector>
#include <icstdlib>
#include <ictime>
using namespace std;
class ThreddFoxx
{
public:
ThreddFoxx();
ThreddFoxx( int iSeqNum );
~ThreddFoxx();
static UINT RunThread( LPVOID params );
UINT tellJoke();
void showJokeNumber();
private:
int m_iSeqNum;
};
ThreddFoxx::ThreddFoxx() { m_iSeqNum = -1; }
ThreddFoxx::ThreddFoxx(int iSeqNum) {
m_iSeqNum = iSeqNum;
srand( (unsigned int)time(NULL) + m_iSeqNum );
m_iSeqNum = rand() % 10 + 1;
}
ThreddFoxx::~ThreddFoxx() {}
UINT ThreddFoxx::RunThread( LPVOID params )
{
ThreddFoxx *tf = reinterpret_cast
(params);
tf->tellJoke();
return 0;
}
UINT ThreddFoxx::tellJoke()
{
// Do random sleep times to make
// this example a bit more realistic
Sleep(m_iSeqNum * 1000);
cout << "How many lightbulbs does it take to screw a tester? "
<< m_iSeqNum << endl;;
return 0;
}
void ThreddFoxx::showJokeNumber()
{
cout << "Joke number is " << m_iSeqNum << endl;
}
// This struct does nothing more than
// map the ThreddFoxx instance to
// its thread handle.
struct THREADTRACKER
{
ThreddFoxx threddFoxx;
CWinThread *pThread;
};
int main( int argc, char **argv )
{
// Create a vector to hold our array
// of THREADTRACKERs
vector
vThreads;
// Create and initialize 10 ThreddFoxxes
for ( int i = 0; i < 10; i++ )
{
ThreddFoxx x(i);
THREADTRACKER tt = {x, NULL};
vThreads.push_back(tt);
tt.threddFoxx.showJokeNumber();
}
// Now kick off all of the threads at once
for ( unsigned int i = 0; i < vThreads.size(); i++ )
{
vThreads[i].pThread =
AfxBeginThread(
ThreddFoxx::RunThread,
reinterpret_cast(&vThreads[i].threddFoxx));
}
// Now wait for them to complete...remember that each
// has a random Sleep time...although we are just doing
// this in order.
for ( unsigned int i = 0; i < vThreads.size(); i++ )
{
WaitForSingleObject(vThreads[i].pThread->m_hThread, INFINITE);
}
// Just to prove here that the class holds it own state
for ( unsigned int i = 0; i < vThreads.size(); i++ )
{
vThreads[i].threddFoxx.showJokeNumber();
}
return 0;
}
% ThreddFoxx.exe
How many lightbulbs does it take to screw a tester? 1
How many lightbulbs does it take to screw a tester? 1
How many lightbulbs does it take to screw a tester? 4
How many lightbulbs does it take to screw a tester? 4
How many lightbulbs does it take to screw a tester? 4
How many lightbulbs does it take to screw a tester? 4
How many lightbulbs does it take to screw a tester? 7
How many lightbulbs does it take to screw a tester? 7
How many lightbulbs does it take to screw a tester? 8
How many lightbulbs does it take to screw a tester? 10
Joke number is 4
Joke number is 8
Joke number is 1
Joke number is 4
Joke number is 7
Joke number is 1
Joke number is 4
Joke number is 7
Joke number is 10
Joke number is 4
A few caveats you should be aware of before trying this code. This is an MFC console application. I used Visual Studio 2008 on a Win 7 platform. You can use the wizard to create an MFC application, but in my case, I created an empty project at first. I then had to make one change in my Visual Studio Project->Properties->Configuration Properties->Code Generation->Runtime Library setting. Set it to Multi-threaded (/MT). The rest should be cut-and-paste, compile, and the run. Good luck.

Recent Comments