Pointer-to-Member functions & pthreads in C++

A common development problem, and one that is becoming even more important as processors gain more cores, and computers themselves gain more processors, is the issue of concurrency. Dealing with concurrency, that is to say multithreaded programming, is vitally important for obtaining top level performance on modern systems. Even in cases where performance isn’t critical, multithreaded programming (especially for GUIs) can vastly improve the user experience of an application. In this part 1 of a series on concurrent programming with C++, I examine what makes using pthreads with C++ difficult at times, and suggest two methods for easily integrating pthreads with an Object-Oriented C++ application, along with an in-depth look at pointer-to-member function syntax and in the light of this practical application.


Writing multithreaded applications is hard. Luckily it’s the same sort of hard that applies programming in general, which is to say that the bulk of the difficulty comes from learning how to think about problems correctly in order to know when and how to apply multithreading. It’s beyond the scope of this particular article to go into the design and architectural aspectects of multithreading an application, but the difficulty of mixing threading with C++ extends beyond the conceptual aspects and into the meat of the development.

If you’ve come from a background of developing threaded applications in Java, or perhaps one of the more esoteric concurrent programming languages like Erlang, or to a lesser extent, from a pure C background, you might wonder why threading in C++ is so difficult. To start with, let’s look at a bit of sample code:

#include
#include
using namespace std;

class example
{
	public:
		void say_something(char* something);
};

void example::say_something(char* something)
{
	cout<<something;
}

int main(int argc, char* argv[])
{
	example my_example;
	pthread_t my_thread;
	char message[] = {"hello world"};
	pthread_create(&my_thread,NULL,&my_example.something,s);//BROKEN!
	return 0;
}

If you were familiar with languages like Java or C, you might wonder why this wouldn’t work. To a C++ developer who has been dealing with the problem of getting C++ code to play nicely with threads, it’s an example of the difficulty of multithreading C++ applications. There are a few different problems with this code. Although some would say that it’s a syntax error, writing code like this, although not syntactically correct, is more of a symptom of misunderstanding the fundamental way that pthreads work, as well as difficulty that comes with mixing C libraries into C++ applications.

Often times examining the code more closely can help one to arrive at a solution, so let’s look at what the code is trying to do, and what might be the root cause of the failure. Fundamentally, what we have is a class, with a member function called say_something which takes a char* as an argument, and prints that char* to the screen. We want to run this function in it’s own thread, and then exit. Now that we know what we want to do, let’s look at why this particular implementation will not work. The compiler will generally give a single error and give up, but there are in fact a few different layers of errors that need to be addressed. Most of these errors come from the definition of the pthread_create function, so to start with, let’s take a look at the prototype for the function.

int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*start_function)(void*), void* args);

The pthread_create function takes an address of a pthread_t, attributes (which are generally NULL), a pointer to a function that returns a void pointer, and takes as it’s argument a single void pointer, and a void pointer to the argument to the function. So, of course, even were it not a class member, our say_something function, which returns void and takes a cstring would not have worked. The real problem is that pthread_create expects a pointer to a function, but we are trying to pass it a pointer to a member function.

If the problem that this poses is immediately apparent to you, feel free to skip down to the end of this paragraph and peruse the code below, but if you don’t immediately understand why this is a problem read on, it is one of the cruxes of understanding the hows and whys of multithreaded development in C++. Essentially, the issue comes down to whether the function has access to an implicit this pointer. Global functions, and static member functions are not associated with any particular object. You can, therefore, easily take the address of them, make pointers to them, and use them as callbacks. Class member functions, on the other hand, need a little bit of extra information; they need a this to associate themselves with. We can’t take the address of my_example.something because we lose the information related to the this pointer that the member function is associate with. The following code snippet may help to illustrate the point of the necessity of a this pointer.

#include
#include
using namespace std;

class example
{
	public:
	string s;
	void say_hi()
	{
		cout<<"hello "<*foo)();
	}
};

int main()
{
	example foo;
	example bar;
	foo.s = "world";
	bar.s = "there";
	foo.get_hi();
	bar.get_hi();
	void ((example::*f)()) =  bar.func();
	(foo.*f)();
	return 0;
}

Compiling and running this application gives the following illuminating output:

hello world
hello there
hello world

As you can see, although we took a function pointer from bar which had it’s string set to “there”, the output still shows foo’s string of “world”. By calling foo.f we are telling f to use foo as it’s this pointer. If it all sounds quite confusing and way too much trouble to bother with, well that’s because it usually is. You don’t often see code like the above snippet precisely because it’s difficult to read, and doesn’t buy you much in practice (There are a few cases where this is incredibly handy, and if you run into one of those situations you will now recognized it and be prepared). The reason for this was to illustrate why things we think should work when it comes to mulithreading with C++ don’t work.

So, you might think, that was a long winded explanation about why you can’t do something. The astute reader however will realize that by going into detail about why things don’t work, I have layed out the pieces to one of the most common solutions to making them work. As you may recall above, I pointed out that the limitation of requiring a valid this pointer applies only to non-static member functions. We’ve also seen that we can call a pointer-to-member-function if we explicitly provide a this pointer by using the -> or . operators. This opens up the groundwork for two solutions, one of which is easy to follow and implement, the other quite powerful.

For the first case, let us assume that we have a standard path of execution that a class will take, and that we simply want to run this path in it’s own thread. In this case we can implement a solution very similar to implementing the Runnable interface in Java. By creating a static class member function that takes a pointer to an object, we can simple use our pthread_create on this static function, and have the function call the appropriate entrance function of the data member it gets as it’s argument. Look at the code below to illustrate this point:

#include
#include
#include
using namespace std
class example
{
	public:
		string s;
		void say_hi()
		{
			cout<<"hello "get_hi();
			return NULL;
		}
};

int main()
{
	pthread_t launch_thread;
	example foo;
	foo.s = "world";
	pthread_create(&launch_thread, NULL, &example::run, static_cast(&foo));
	pthread_join(launch_thread,NULL);//added so we can see the output before we exit
	return 0;
}

In this example, we have added a static function. Since we can take the address of a static member function and treat it like a normal global function, we can easily pass this function into our pthread_create. This lets us treat each instance of the class as a single threadable unit. We can spawn off a thread to begin execution, and then let our class do it’s thing. This simple approach can solve many issues that might crop up in implementing concurrency in a C++ application. There are drawbacks however.

The previous approach limits in many ways. First of all, we are limiting ourselves to a single entry point into the class. If we want to spawn off more threads from other places, we need to create more static functions. We also need to know at compile time what functions we are going to want to call in threads. If we had a dozen possible entry points where we might want to spawn off a thread, the codebase could grow very large and very complex.

Fortunately, since we understand how to use pointer-to-member-functions, we have the benefit of another solution. Consider the following code snippet:

#include <iostream>
#include <string>
#include <pthread.h>
using namespace std;

class example
{
        public:
                string s;
                void say_hi()
                {
                        cout<<"hello "<<s<<endl;
                }

                void (example::*func())()
                {
                        return &example::say_hi;
                }

                void get_hi()
                {
                        void ((example::*foo)()) =  func();
                        (this->*foo)();
                }

                static void* thread_maker(void* dat_s);
};

typedef struct val_st
{
        example* e;
        void (example::*func)();
};

//moved this out of the class definition since we need to reference val_st structure
void* example::thread_maker(void* dat_s)
{
        struct val_st* v = static_cast<val_st*>(dat_s);
        example* e = v->e;
        void ((example::*f)()) = v->func;
        (e->*f)();
        return NULL;
}

int main()
{
        pthread_t launch_thread;
        example foo;
        example bar;
        val_st v;
        foo.s = "world";
        bar.s = "there";
        foo.get_hi();
        bar.get_hi();
        void ((example::*f)()) =  bar.func();
        (foo.*f)();
        v.e = &foo;
        v.func = foo.func();
        pthread_create(&launch_thread, NULL, &example::thread_maker, static_cast<void*>(&v));
        pthread_join(launch_thread,NULL);
        return 0;
}

In this case, we consider what we know about pointer-to-member-functions, and the this pointer, and exploit this to vastly simplify a class that might have multiple thread entry points. In order to do this, the first step is to add a structure to our application. This structure contains a pointer to a member function, and the object to become that member-function’s this pointer. This enables us to create a single static thread_maker function. We pass our structure (cast as a void*) to this static function which is spawned off in it’s own thread. We now have the ability to spawn off into a thread any arbitrary member function. We can even decide at run time what functions to spawn off. In performance-critical applications, we reduce the overhead of having layers-deep logic to bend all of our code around a single entry point, and trade off dozens or more functions with a single, slightly more difficult to read function. We now have the ability to create pthreads in C++ in a fast, concise, and elegant manner.

Comments are closed.