2

How can I call AfxBeginThread with an arbitrary non-static class method? Maybe there is something I can do with boost bind? Below is the expected usage from Microsoft (and is an example of calling a non-static method but it is hard-coded which method):

UINT MyThreadProc( LPVOID pParam )
{
    CMyObject* pObject = (CMyObject*)pParam;

    if (pObject == NULL ||
        !pObject->IsKindOf(RUNTIME_CLASS(CMyObject)))
    return 1;   // if pObject is not valid

    // do something with 'pObject'

    return 0;   // thread completed successfully
}

// inside a different function in the program
...
pNewObject = new CMyObject;
AfxBeginThread(MyThreadProc, pNewObject);
User
  • 62,498
  • 72
  • 186
  • 247
  • You already have the solution. You posted it in the question. In what way is it not sufficient for you? – David Heffernan Oct 21 '11 at 18:20
  • What I'd like to be able to do is execute an arbitrary class member function in a thread like you can do with boost thread. My posted example works but it's hard coded to a particular class. I would like to use boost thread but it seems there are some issues when using it in an MFC context. – User Oct 21 '11 at 18:48
  • You have to call your member function where there is the placeholder comment "do something with 'pObject'". Sorry, but that's just the way it is. – David Heffernan Oct 21 '11 at 18:52

3 Answers3

5

You will need a static function to pass to AfxBeginThread, but it can be a very simple function that calls into the object. Here's an untested template function that might work.

template<class T>
UINT __cdecl StartThread(LPVOID pParam)
{
    return ((T*)pParam)->MyThreadProc();
}
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
2

Your code sample is fine. You will need a thread function for each different non-static class method you want to call in separate threads.

boost:bind will not help you whatsoever... AfxBeginThread would have to be a C++ template function, otherwise it cant be compatible with boost::bind or C++11 lambdas with captures.

One Alternative is to create a struct, with an enum for each class/method combination you will have, but this still requires you to manually add code to both the enum and the callback function for each class/method combination. However its not that much less code than creating a separate thread function for each class/method combination.

struct ThreadData
{
  LPVOID object;
  enum ObjectCallType {
    Foo_Foo,
    Foo_Bar
  } objectCallType;
  LPVOID* param;
  ThreadData( LPVOID pobject, ObjectCallType poct, LPVOID* pparam=0 )
  :object(pobject), objectCallType(poct), param(pparam) {}
};

UINT MyThreadProc( LPVOID pParam )
{
    TheadData* thData = (ThreadData*)pParam;
    try 
    {
        switch( thData->objectCallType )
        {
            case ThreadData::Foo_Foo:
                Foo* foo = (Foo*)thData->object;
                foo->foo();
                break;
            case ThreadData::Foo_Bar:
                Foo* foo = (Foo*)thData->object;
                foo->bar( thData->param );
                break;
            default:
                throw std::exception("unhandled method call type");
        }
    }
    catch( std::exception& e )
    {
        std::cerr << e.what() << std::endl;
        delete thData;
        return 1;
    }
    delete thData;
    return 0;
}



//usage:
AfxBeginThread(MyThreadProc, new ThreadData(myFooObject,ThreadData::Foo_Bar,myFooCallParam));

Boost example (untested):

boost::thread myFooFooThread( boost::bind( &Foo::Foo, myFooObject ) );
smerlin
  • 6,446
  • 3
  • 35
  • 58
  • I don't have experience with boost bind. Could you pass a boost bind object as the parameter to `MyThreadProc` and then have MyThreadProc simply call the boost bind function? – User Oct 21 '11 at 17:47
  • @User: no you cant, because you would have to cast the boost::bind object from LPVOID to the actual object, and thats not trivial, because pretty much every boost::bind object has a different (complicated) type. You can however use boost::bind objects with boost::thread. – smerlin Oct 21 '11 at 17:57
  • given that I wonder if you could use boost::bind with a templated StartThread function as in Mark Ransom's answer. – User Oct 25 '11 at 05:10
  • @User: yes you can, but you will still have to specify the type of the bind expression as template parameter, and this is very troublesome, unless you can use the C++11 features `auto` and `decltype` or `std::function`. – smerlin Oct 25 '11 at 06:54
0

I know this question is pretty old -- but it's close to my current situation. I am working on an application written in Visual Studio 2008 project, and would like to avoid all the start functions.

What I found is there are two different invocations of AfxBeginThread: One requires the start function (for launching working threads); the other takes, as its first parameter a class derived from CWinClass and is used for creating User Interface connected objects AND worker threads.

I am choosing the second option. Would this not also work in the above question?

Charles Thomas
  • 965
  • 10
  • 11