Wrapper templates to make writing callback functions slightly easier


I previously discussed why callback functions must be static if they are member functions.

Writing the correct prototype for the callback function is usually somewhat clumsy. It’s not hard. Just clumsy.

class Sample {
 static DWORD CALLBACK StaticThreadProc(LPVOID *lpParameter)
 {
  Sample *self = reinterpret_cast<Sample*>(lpParameter);
  return self->RealThreadProc();
 }
 DWORD __stdcall RealThreadProc()
 {
   ... do stuff ...
 }
 void DoSomething()
 {
   ... CreateThread(NULL, 0, StaticThreadProc, this, 0, &dwTid); ...
 }
};

(If you read my previous article, you’d recognizing sticking a __stdcall in the declaration for RealThreadProc as a micro-optimization.)

Every class that has a thread procedure needs this “trampoline” function StaticThreadProc that has the correct function signature, then massages it into something that is easier to work with (in this case, an object pointer and method call). Well, okay, you could do the work directly in the trampoline if you wanted to, but it’s usually much more convenient to put the bulk of the work in a proper member function so you have access to all the “this” shorthand.

If you do this a lot, you can write a template function to do the boring grunt work, freeing your brain to do “real thinking”.

template<class T, DWORD (__stdcall T::*F)()>
DWORD CALLBACK ThreadProc(void *p)
{
    return ((reinterpret_cast<T*>(p))->*F)();
}

This template function declares a templatized thread procedure. Notice that the calling convention for the ThreadProc template function is correct for a thread function, so this guy can be passed straight to CreateThread. Your class then would look like this:

class Sample {
 DWORD __stdcall Background()
 {
   ... do stuff ...
 }
 void DoSomething()
 {
   ... CreateThread(NULL, 0, ThreadProc<Sample, &Sample::Background>, this, 0, &dwTid); ...
 }
};

This takes the trampoline function out of the class declaration. Instead, it is auto-generated by the compiler via the template.

Okay, so maybe this isn’t much of a typing-savings after all, considering the rather clumsy expression for the template invocation. I wonder if it can be made simpler.

[Raymond is currently on vacation; this message was pre-recorded.]

[2004 July 31 – fix obvious typos.]

Comments (18)
  1. Centaur says:

    Many frameworks define a Thread class or template that takes care of all the Win32 interfacing, leaving the developer to override the main function (and constructor to take additional parameters).

    Here’s an example class documentation, PThread from PWLib.

    http://www.openh323.org/docs/pwlib/PThread.html

  2. icymint3 says:

    good, very good

  3. Chris Becke says:

    We really just need to slap the twits in charge of the compiler standard who decide that they can’t let us at nifty things like the ability to assign "this" in a static and use class data thereafter.

    :P

  4. ATZ Man says:

    Mat,

    Obviously there are languages and frameworks and so on that do indeed take care of the details for you. You have your C#, your Java, your scripting languages (the old fashioned term for Perl and Python). You can write frameworks for C++ that do this, too. Raymond writes for a Win32 C++/C audience.

  5. ATZ Man says:

    Follow-up question:

    What of beginthread? As I understand it, if you use operator new or malloc or one of many standard library functions you need to use beginthread[ex] to launch a thread instead of CreateThread.

  6. Mat Hall says:

    I know we have a range of things, from Lisp at one end where everything’s some sort of list, and you don’t need to get involved in the nitty gritty of how it actually works, via C (which combines the power of assembly language with the ease of use of assembly language) through to raw binary, each with their own special powers…

    My point (such as it was) that in this day and age, what business of anyone’s is it whether foo is an int, a pointer to an int, a dereferenced pointer to an int, or a collection of Uraguayan stamps? How come we’re this far into the 21st century and people still need to know this sort of thing? Where’s my meal in a pill? My jetpack? Have we actually made any progress, or can we just waste time much faster than before?

    I think I shall join a monastery — at least they make no pretence at progress, and it’s the same nonsense it ever was…

    (And I’m still having a bad day. I’ve been given a problem that’s most definitely not a nail, and the only tool I’m allowed is a hammer. (A time-and-motion manpower resource estimation app, and Access 97. And somebody told the guy who wants it about XML, and he seems to think its relevant.) Understandably, I’m bitter.)

    Apologies for the interuption, and we now return you to your scheduled broadcast.

  7. asdf says:

    How come the security attributes in CreateThread isn’t a pointer to const?

  8. Michael Kohne says:

    Use a macro. Yea, bad old #define:

    #define STATIC_THREAD_PROC(ClassName) static DWORD CALLBACK StaticThreadProc(LPVOID *lpParameter)

    {

    ClassName *self = reinterpret_cast<ClassName*>(lpParameter);

    return self->RealThreadProc();

    }

    Invoke thusly in the class header:

    STATIC_THREAD_PROC(Sample)

    You are trying to do text replacement, so use a tool that does text replacement. And it’s easy to use.

  9. Mat Hall says:

    It’s things like that that make me wonder why anyone uses C (or its derivatives) any more. Have we not prgoressed far enough that the compiler is smart enough to handle all this sort of tedious junk for us? If you (almost) *always* have to do this, why can’t it be done for you? Why must we have pointers everywhere?

    The way I see it, in the years since I started coding computer power has increased by at least two orders of magnitude, so why now do I still need to bust my ass doing all this when the hundred-fold increase in machine speed can compensate for me only being human and not really being designed for handling this sort of totally abstract and unneccesarily complicated state of affairs? Why can’t programming languages or OSes compensate for sloppy coding? If my handle goes out of context, why can’t the machine deal with freeing it? Why should I have to bother? SO I didn’t free up some memory. Big deal. From context it’s obvious I won’t be using it again, so use those cycles for something useful! I have far more important things to worry about!

    Spock with a beard? Has the universe gone crazy?

    (Apologies. I’ve had a bad day, and I’m on the cusp of abandoning computers for good — they may be faster and more capable than ever, but they’re still as awkward and boneheaded as ever, and I can’t take it any more!)

  10. DrPizza says:

    I personally do not like to lose the void* parameter. I don’t like to lose it because I sometimes have more than one thread per member function, so it can be useful to pass context information.

    I also do not like to put the trampoline inside the class (as I don’t really see why it belongs there).

    And finally I do not like to type in template parameters if I can help it.

    So I do something along the lines of:

    template <typename T, typename P, typename F>

    struct ThreadInfoMemberFn

    {

    typedef T class_type;

    typedef P parameter_type;

    typedef F function_type;

    T* obj;

    function_type function;

    P data;

    ThreadInfoMemberFn(class_type* f, function_type fp, parameter_type d) : obj(f), function(fp), data(d) {}

    private:

    ThreadInfoMemberFn<T, P, F>& operator=(const ThreadInfoMemberFn<T, P, F>&);

    };

    template <typename T, typename P, typename F>

    DWORD WINAPI ThreadDispatchMemberFn(void* data)

    {

    try

    {

    std::auto_ptr<ThreadInfoMemberFn<T, P, F> > ti(static_cast<ThreadInfoMemberFn<T, P, F>*>(data));

    return ((ti->obj)->*(ti->function))(ti->data);

    }

    catch(…)

    {

    return 0;

    }

    }

    template <typename T, typename P, typename P2>

    HANDLE CreateThread(SECURITY_ATTRIBUTES* sa, SIZE_T stackSize, T* obj, DWORD(T::* func)(P), P2 data, DWORD flags, DWORD* tid)

    {

    typedef DWORD(T::* F)(P);

    std::auto_ptr<ThreadInfoMemberFn<T, P, F> > ti(new ThreadInfoMemberFn<T, P, F>(obj, func, data));

    HANDLE rv(::CreateThread(sa, stackSize, ThreadDispatchMemberFn<T, P, F>, static_cast<void*>(ti.get()), flags, tid));

    if(NULL != rv) { ti.release(); }

    return rv;

    }

  11. Ben Hutchings says:

    ATZ Man: Whenver a thread exits, the DllMain function of each DLL in the process is called, allowing it to clean up thread-specific resources. _beginthread(ex) is a kluge to make this happen if you link the C/C++ run-time as a static library. You don’t need to use it if you use a DLL version of the run-time.

  12. Raymond Chen says:

    The problem with making RealThreadProc virtual is that it means that each class can have only one thread procedure. (What if you want two background threads? What do you call the second one?)

  13. Waleri says:

    Why not make the RealThreadProc() simply a virtual memeber?

    In this case all template issues are gone. You can derive from the base class, override RealThreadProc() and you can use your own construct to initialize the instance with all the arguments you need.

    In any case, whether one uses a template or any other class to create a thread, one should make sure class didn’t went out of scope before thread is terminated.

    In fact none of the above didn’t make my life easier.. why would I need it then at all?

    I think most reliable solution is a template that would create a *copy* of my class in the *new* thread and delete it upon termination – something like:

    class CMyClass : public CThread

    {

    public: virtual long ThreadEntryPoint(void);

    }

    TCreateThread<CMyClass> thread = CMyClass(bla,bla);

    or if you don’t like things in contructors

    TCreateThread<CMyClass> thread;

    thread.CreateNewThread(CMyClass(bla,bla));

    in this case

    a) I don’t have to worry that object could go out of scope before thread terminates

    b) it is typesafe

    c) I am not limited to one argument only

    Of course TCreateThread<> will have to be implemented in a way that will make sure that CreateNewThread() won’t return control before newly created thread is created, started and has created copy of the argument, but this isn’t hard to do using one mutex and one event

  14. mschaef says:

    "I know we have a range of things, from Lisp at one end where everything’s some sort of list, and you don’t need to get involved in the nitty gritty of how it actually works, via C "

    Not to be too pedantic, but modern Lisp’s (<20 years old, let’s say) have a fairly diverse set of capabilities. While they do have support for lists, they also typically have elaborate support for vectors/arrays, hash tables, structures, and objects. Compilers matching the performance of C aren’t atypical either, but they’re typically more interactive than what you’re used to. It’s possible to define a function in one interactive command, compile it in the next, and disassemble it (to native x86 machine code, for example) in the third line.

    The net result of this is that it’s easy to think in terms of lists and interpreted code while doing prototyping/design, and switch to faster data structures and native compiled code for the speed critical stuff.

    It’s also worth mentioning that many Lisp implementations are written entirely in Lisp and compiled to native code by themselves (see Scheme48 and PreScheme), it’s not below the "nitty gritty". A lot of the same things can be said of Smalltalk too… squeak is almost entirely written in Smallwalk, IIRC.

  15. Dan says:

    I’m surprised no one has mentioned Hickey’s "Callbacks in C++ Using Template Functions." I originally found it in C++ Gems years ago. Although a bit dense and difficult to deal with it’s come in quite handy. Here’s one reference to it: http://www.tutok.sk/fastgl/callback.html

  16. Dan says:

    I’m surprised no one has mentioned Hickey’s "Callbacks in C++ Using Template Functions." I originally found it in C++ Gems years ago. Although a bit dense and difficult to deal with it’s come in quite handy. Here’s one reference to it: http://www.tutok.sk/fastgl/callback.html

  17. Waleri says:

    I can’t retrieve pointer to ThreadProc<>

    Any ideas?

  18. Raymond Chen says:

    I fixed some obvious typos (wrong return value). The point was to use the template; I figured my readers would be smart enough to fill in the gaps.

Comments are closed.