Ask the C++ Pro 10-Minute Solutions

Executing an Object's Member Function in a Separate Thread
By Danny Kalev

The Problem
Virtually all thread libraries were designed to handle C functions, not C++ class member functions. A typical library provides an API function that takes a pointer to a callback function as a thread's execution code and invokes that callback function in a separate thread. The problem is that you can't create a thread that executes an object's member function in such thread libraries; you can only use plain functions. For this reason, the following code fails:


// a thread launching function of a typical thread library
int thr_create (void (*pf)(), void* prm, thread_t* pth);

#include "class1.h"

int func (void *param )
{
 thread_t t1;
// the following call results in a compilation error:
// "Cannot convert 'void (class1::*)()' to 'void (*)()'"
 return thr_create ( &class1::some_method, param, &t1);
}

The function thr_create() expects an address of a callback function, void* as an address of the argument list to be passed to the callback function, and a pointer to a thread_t variable (for more information on callback functions and function pointers, read this 10-minute solution).

The code above fails to compile because the first argument passed to thr_create() is a pointer to a member function of class1, not a pointer to an ordinary function. As you probably know, ordinary functions and member functions are two entirely different things. Even a brute-force cast won't do in this case. How can you solve this problem?

Solution 1: Use a Static Member Function
The first solution is to make the callback member function static. A static member function doesn't take an implicit this argument, as do ordinary member functions. Therefore, you can take its address and treat it as if it were a pointer to an ordinary function. If you need to access an object's data member from the static member function, pass the object's address explicitly as an argument of the static function, for example:


class Hack
{
private:
 int x;
public:
 int get_x();
 static void func(Hack * pthis); // static member function
 void func2(); // nonstatic member function
};

void Hack::func(Hack * pthis)
{
 int y = pthis->get_x(); // access an object's data member
}
 

Although this solution works for most cases, sometimes you can't make the member function static, e.g., when the member function is virtual or if you're using a third-party class that you can't modify. Fortunately, there is a way to get around this, except that in this case you will have to work a little harder.

Solution 2: Handling a Nonstatic Member Function
Suppose you need to invoke the member function func2(), a nonstatic member function of class Hack, in a separate thread. Instead of directly passing the member function's address to thr_create(), declare an ordinary function that takes void *, let's call it intermediary():


void intermediary(void*);

Next, create a struct that packs a pointer to the member function in question and a pointer to the class object:


struct A
{
 Hack * p;
 void (Hack::*pmf)(); // a pointer to a member function
};

Create an instance of that struct and fill it with the addresses of the desired object and its member function (you can read more on pointers to member functions in this 10-minute solution) as follows:


A a; // struct instance
Hack h; // create an object
//fill struct
a.p = & h; 
a.pmf = &Hack::func2; // take address of member function

Now back to the implementation of the intermediary() function:


void intermediary(void* ptr)
{
 A* pa=static_cast < A* > (ptr); // cast p to A* 
 Hack* ph=pa->p; // extract address of Hack object from A
 void (Hack::*pmf)()=pa->pmf; // extract ptr to member func
 (ph->*pmf)(); // call the member function
}

Finally, pass the address of intermediary() to thr_create():


thr_create (intermediary, (void*) &a, &t1 );

thr_create() invokes the function intermediary()and passes A's address to it. intermediary() in turn unpacks the A struct from its pointer argument and invokes the desired member function. The extra level of indirection enables you to safely launch a member function in a separate thread even if your thread library doesn't support member functions. If you need to launch different member functions of different classes, you can turn the A struct into a class template and intermediary() into a function template. This way, the compiler will generate most of the boilerplate code for you.

 
Other 10-Minute Solutions
 How to Change the Mouse Pointer without Flicker
 Setting Full Row Selection in ListView Control
 Automating Type Conversions with stringstream Objects
 Improving Memory Reallocation with Vectors
 How to Use <fstream> Classes for File I/O
 Casting About for Safe Typecasting
 Overloading Operator + the Right Way
 How to Create Persistent Objects
 Making Linked Lists More User-Friendly
 Preventing Glitches in Signal Processing
 Forcing Object Allocation on the Free-store
 Using String-Based Data Validation
 Implementing the 'Resource Acquisition Is Initialization' Idiom
 Simple Locks for Data Files
 Template Specializations
 Exception Handling
 Using Bit Fields in Data Optimization
 Using the Transform() Algorithm to Change a String's Case
 Use RTTI for Dynamic Type Identification
 Choosing the Right swap () Implementation
 Take Charge and Initialize Your Own Data
 Share Data Among Objects Using the Monostate Design Pattern
 String Manipulation Made Easy with std::string Algorithms
 Using typedef to Curb Miscreant Code
 Managing Objects' Construction Order
 Bitwise Operators: Combining Efficiency and Ease of Use
 Use Function Adapters to Extend Generic Algorithms' Usage
 Simplify Callback Dispatching with Enumerated Indexes
 Streamline Your Bulk I/O Operations with Stream Iterators
 Optimize Your Member Layout
 Preserve Code Safety with Conversion Operators
 Modify Your Base Class Interface in Derived Classes
 Tackle Common Programming Tasks Using the New <tuple> Library
 Use Local Classes for Proper Cleanup in Exception-enabled Apps
 Use multimap to Create Associative Containers with Duplicate Keys
 Enforcing Compile-time Constraints
 Facilitate Directory Operations with the <dirent.h> and <dir.h> Libraries
 Spruce Up Your Built-in Arrays
 Target 32- and 64-bit Platforms Together with a Few Simple Datatype Changes
 Restrict Object Allocation to Specific Memory Types
 Use the Pimpl Idiom to Reduce Compilation Time and Enhance Encapsulation
 Automate Resource Management with shared_ptr
 The Quick and Dirty Way to Add
 Pointing to Class Members
 Detecting Keystrokes While Your Application is Busy
 Linked Lists
 Programming the System Tray
 Create a "Universal" DLL
 Convert Path to Long Path Name
 Constructing an Object at a Pre-Determined Memory Position
 Declaring Classes and Member Functions in a Namespace
 Using the auto_ptr Class Template to Facilitate Dynamic Memory Management
 Using the random_shuffle() Algorithm to Randomize a Sequence of Elements
 Defining a Function Object
 Implementing the Singleton Design Pattern
 Declaring Function Pointers and Implementing Callbacks
 Overloading Operator << for a User-Defined Type
 Implementing a Stopwatch Class for Performance Measurements
 Creating and Accessing Environment Variables
 Executing an Object's Member Function in a Separate Thread
 Creating Heterogeneous Containers
 Overriding New and Delete
 Time and Date Manipulation
  Defining Functions with a Variable Argument List
 Optimize Abstract Operations with Function Templates


Ask the C++ Pro | Who is the Pro? | Usage Policies | Search | Feedback


Sponsored Links


Advertising Info  |   Member Services  |   Contact Us  |   Help  |   Feedback  |   Site Map
Jupiterweb networks

internet.comearthweb.comDevx.comClickZ

Search Jupiterweb:

Jupitermedia Corporation has four divisions:
JupiterWeb, JupiterResearch, JupiterEvents, and JupiterImages

Copyright 2004 Jupitermedia Corporation All Rights Reserved.
Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

Jupitermedia Corporate Info | Newsletters | Tech Jobs | E-mail Offers