In between midnight summer nights and dark winter days

Creating pthreads in C++ using pointers to member functions Fri, Sep 03 2010 10:10

I occasionally come across the need for creating threads that use a C++ member function as its entry point. The traditional way of doing this is to define a static member function as part of the class, and then have that method call the member function. For instance:

// Thread.h:
class Thread {
  public:
    ..
    static void*  callMemberFunction(void *arg) { return ((Thread*)arg)->memberFunction(); }
    
    void*         memberFunction(void);
    void          startThread(void);
};

// Thread.cpp:
void* Thread::memberFunction(void) {
  // Do actual work here
  return 0;
}

void  Thread::startThread(void) {
  pthread_t tid;
  int       result;
  result = pthread_create(&tid, 0, Thread::callMemberFunction, this);
  if (result == 0)
     pthread_detach(tid);
}

This is fine for one-offs, but I dislike the need for a separate static member function for each thread I want to create, especially for objects that kick off multiple threads. It just doesn't seem very elegant to me, and it needlessly bloats the code.

In C++, member functions are always called with an implicit argument: this. Given that pthread_create allows us to pass a single argument to the thread, why can't we just do something like this?

// Thread2.h
class Thread2 {
  public:
    ..
    void *memberFunction(void);
    void startThread(void);
};

// Thread2.cpp
void* Thread2::memberFunction(void) {
  // Do work here..
  return 0;
}

void  Thread2::startThread(void) {
  pthread_t tid;
  int       result;
  result = pthread_create(&tid, 0, &Thread2::memberFunction, this);
  if (result == 0)
     pthread_detach(tid); 
}

Well, the answer lies in the type the compiler assigns to the function pointer for Thread2::memberFunction. In our case, the type is void *(Thread2::*)(void), while the type expected by pthread_create is void *(*)(void *). Attempting to cast the member function pointer doesn't work, since the compiler ends up complaining. However, there is a way we can make it work, if we can live with a bit of hackery, the risk of things blowing up in our face at any time, and probably some other caveats that I'm not presently aware of.

The solution is to define two function pointer types, and then circumvent the compiler by manually copying the function pointer to a variable of the appropriate type. Consider the following:

typedef  void* (Thread2::*Thread2Ptr)(void);
typedef  void* (*PthreadPtr)(void*);
 
void  Thread2::startThread(void) {
  Thread2Ptr   t = &Thread2::memberFunction;
  PthreadPtr   p = *(PthreadPtr*)&t;
  pthread_t    tid;
  if (pthread_create(&tid, 0, p, this) == 0)
    pthread_detach(tid);
}

The above compiles and works correctly, at least on Mac OS X and Linux, and so far without having blown up in my face. Good times! What is happening behind the scenes here is, essentially that the compiler copies the data in t to p, circumventing the typecheck in the process. We could also have implemented the above using memcpy: memcpy(&p, &t, sizeof(void*)).

Now, as mentioned above, there are probably many good reasons not to do this. I have found this to be a good resource: Pointers to member functions. The FAQ lists a number of good reasons NOT to use them. I still do though, at least for the special case of creating threads.



Previous: Quickly open URLs in Terminal Next: Making Universal Back Button work on 10.6.5 and later

About me
My name is Daniel Stødle. I live in Tromsø, Norway, at 69.66° North. By day, I work as a researcher at the Northern Research Institute in Norway; by night, I run my company SCSC. I do most of my development on and for Mac OS X. My research is geared towards interaction with and visualization of geospatial data. Read more on my personal page.

Software

Contact me
E-mail: daniel@scsc.no

Recent posts


2011
Jul 28: Running iTunes in a debugger (gdb)
Jul 25: The /Volumes/MobileBackups directory
Jul 20: FolderGlance 3.0.1 supports Lion
Mar 03: Quick tip: Speeding up Xcode compilations
Mar 02: FolderGlance 3.0
Jan 07: Making Universal Back Button work on 10.6.5 and later

2010
Sep 03: Creating pthreads in C++ using pointers to member functions
May 31: Quickly open URLs in Terminal
May 31: Snow Leopard and automatically submitted Crash Reports
May 27: Universal Back Button released for Mac OS X
May 22: The 22 Megapixel Laptop
Feb 09: FolderGlance on MacUpdate Promo

2009
Sep 28: FolderGlance 2.5.3 is out
Sep 21: FolderGlance 2.5.1 adds features and fixes bugs
Sep 16: FolderGlance 2.5 released!
Sep 10: Intriguing: Snow Leopard ships with the iPhone's multi-touch API built-in
Sep 03: FolderGlance and Snow Leopard
Mar 15: Fixing Keynote '08 and '09 to work with the Scripting Bridge
Feb 26: A website in an image
Feb 09: Display wall multi-touch

2008
Feb 19: Spaces.. Spaces.. Spaces.. retires
Feb 08: How-to: Reverse engineering the Dock to fix Spaces
Jan 25: Interacting with wall-sized displays

2007
Dec 20: Interesting Finder bug
Dec 06: Developing applications for the iPod touch (and the iPhone)
Nov 15: Spaces.. Spaces.. Spaces.. and 10.5.1
Nov 15: Thread creation using pthread_create() on Leopard
Nov 13: Spaces.. Spaces.. Spaces..
Nov 07: FolderGlance, Leopard and the More... menu
Nov 06: FolderGlance and Screen Sieve now also on Leopard!
Sep 16: Mysterious window server hangs

Archive
2007
2008
2009
2010
2011

RSS feed
RSS

Links
SCSC
Blog frontpage