2

I am attempting to call a private member function (should not be available as public or protected) as a worker item with the Win32 function QueueUserWorkItem(). I know I've done this before and it was easy, but now I can't find that snippet, nor can I make bind() voodoo work. So for the purposes of this question the class is:

class Arbitrary {

    public:
        Arbitrary() ;
        ~Arbitrary() ;

        bool    UsefulPublicFunction(unsigned uParameter) ;

    protected:

    private:
        void    PrivateWorkItem(void* pVoid) ;
} ;

And inside UsefulPublicFunction() we might see:

LPTHREAD_START_ROUTINE pThreadStartRoutine ;
ULONG uFlags = 0 ;
void* pContext = nullptr ;

if (QueueUserWorkItem(pThreadStartRoutine, pContext, uFlags)) {
    //blah blah blah
}

Where I seem to go off in the weeds is with the assignment to pThreadStartRoutine with something like:

pThreadStartRoutine = std::bind<&Arbitrary::PrivateWorkItem, this, std::placeholders::_1> ;

I recognize that the signature for PrivateWorkItem probably should change to:

private:
    DWORD   WINAPI  PrivateWorkItem(void* pVoid) ;

Even with that change, no joy. VS2015 really hates the way I'm using bind().

What should my assignment to pThreadStartRoutine look like?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770

2 Answers2

2

This seems to work:

#include <Windows.h>

#include <stdio.h>

#include <functional>

using namespace std::placeholders;

class Arbitrary {

    public:

        bool UsefulPublicFunction(int uParameter);

    protected:

    private:
        typedef std::function<void (void)> CallbackType;
        static DWORD WINAPI ProcessWorkItem(void* pVoid);
        void PrivateWorkItem1(int arg1, int arg2);
        void PrivateWorkItem2(char * arg1);
};

void Arbitrary::PrivateWorkItem1(int arg1, int arg2)
{
    printf("Numbers are %u %u\n", arg1, arg2);
    return;
}

void Arbitrary::PrivateWorkItem2(char * arg1)
{
    printf("String is %s\n", arg1);
    return;
}

DWORD WINAPI Arbitrary::ProcessWorkItem(void* pVoid)
{
    CallbackType * callback = static_cast<CallbackType *>(pVoid);
    (*callback)();
    delete callback;
    return 0;
}

bool Arbitrary::UsefulPublicFunction(int param1)
{
    QueueUserWorkItem(&ProcessWorkItem, new CallbackType(std::bind(&Arbitrary::PrivateWorkItem1, this, param1, 7)), 0);

    QueueUserWorkItem(&ProcessWorkItem, new CallbackType(std::bind(&Arbitrary::PrivateWorkItem2, this, (char *)"This is my string")), 0);

    Sleep(1000);
    return true;
}

int main(int argc, char ** argv)
{
    Arbitrary x;

    x.UsefulPublicFunction(5);

    return 0;
}
Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
1

Try something more like this instead:

class Arbitrary {

    public:
        Arbitrary() ;
        ~Arbitrary() ;

        bool UsefulPublicFunction(unsigned uParameter);

    protected:

    private:
        static DWORD WINAPI PrivateWorkItem(void* pVoid) ;
        void PrivateFunction();
} ;

DWORD WINAPI Arbitrary::PrivateWorkItem(void* pVoid)
{
    static_cast<Arbitrary*>(pVoid)->PrivateFunction();
    return 0;
}

...

if (QueueUserWorkItem(&PrivateWorkItem, this, 0)) {
    //blah blah blah
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • That "feels" rather clunky and to me, does not seem to be as reliable from a long term code maintenance perspective - mainly because there are actually a number of unique private worker functions in the class. But yes, I've done it the way you suggest and I've also declared and auto variable with the correct signature and then just slammed it into pThreadStartRoutine with memcpy but that seems just as clunky to me. To the point, using bind seems straight forward as to intent when an intern maintains this code in 10 years. – BenWestbrook Apr 14 '16 at 22:42
  • You can't really use `std::bind()` to setup Win32 API callback functions (see http://stackoverflow.com/questions/18161680/). What I showed above is the "cleanest" solution that does not involve ugly hacks. – Remy Lebeau Apr 14 '16 at 22:46
  • You could presumably use a single static function that accepts a pointer to a forwarding call wrapper generated by std::bind. Just so long as the call to the wrapper comes from C++ rather than from Windows. – Harry Johnston Apr 15 '16 at 00:15
  • Thank you Remy and Harry for the quick responses. – BenWestbrook Apr 15 '16 at 01:38
  • Going back to the original question, I'd like to add a clarification. The argument to PrivateWorkItem (pVoid) is already spoken for so we can not repurpose it for other uses such as a class pointer. The more I think about this problem the more complicated it seems so I am sure I am missing something obvious. Imagine the class Arbitrary has 30 private work items - how would you solve this? – BenWestbrook Apr 15 '16 at 02:03
  • Harry, when trying to implement your suggestion I run in to the same function pointer issues I started with (which may be 100% my ignorance). – BenWestbrook Apr 15 '16 at 02:05
  • There's no need for `pVoid` to contain the argument to your member function. It can always point to a structure (or more complex object) that contains whatever information you need, including a class pointer *and* the member function argument. I'm not personally familiar with `std::bind` but if I get a chance later on I'll experiment. – Harry Johnston Apr 15 '16 at 03:03