0

I'am writing small class for RS232 port. It can sync write and async read. So, for async read I'm using second thread, that waiting input data. When data received, I want to call user callback (that I get as constructor argument) with input data. It's look like:

typedef int (*ReceivedCallback)(string data);

class RS232
{
    RS232(string portName, ReceivedCallback dataReceived);
    ~RS232();

private:
    ReceivedCallback dataReceivedCallback;

private:
    static unsigned ReaderThread(void* data);

public:
    SendData(string data);
}

My problem is: ReaderThread MUST be static to pass pointer to it to _beginthreadex() function. And in ReaderThread I want to call "dataReceivedCallback", obtained from the user in constructor. But I can't, cause I can't call non-static functions in static ReaderThread. Also, I can't make "dataReceivedCallback" static, cause I may have many instances of my class (for COM1, COM2, COM3) and every instance should have it's own callback, obtained by user.

Where is my architecture mistake? How would you implement it?

Thanks in advance!

P.S. Using Visual Studio 2005.

DeniDoman
  • 179
  • 1
  • 3
  • 13
  • If C++11 is possible, then an std::thread can be started with member function. See http://stackoverflow.com/questions/10673585/start-thread-with-member-function – stefaanv Jul 11 '13 at 13:22

3 Answers3

2

You need to pass a pointer to the instance of RS232 to ReaderThread, which will, in turn, either pass that pointer to a static callback, or directly call non-static methods on the RS232 object.

I would also use [CreateThread][1] rather than beginthreadex. My code samples will use CreateThread, but you can adapt the technique to beginthreadex if you so desire.

Simply, when kicking off the ReaderThread, pass it a pointer to the instance:

RS232* myObj = new RS232;
CreateThread (..., myObj);

...use reinterpret_cast to cast it back:

unsigned RS232::ReaderThread (void* data)
{
  RS232* that = reinterpret_cast <RS232*> (data);
}

Change your callback function so that it can pass the instance as well:

typedef int (*ReceivedCallback)(string data, RS232* that);

And now in the callback you can call member functions:

that->DoSomethingCool (data);
John Dibling
  • 99,718
  • 31
  • 186
  • 324
1

You will need to pass an extra argument to the thread function (which you already have a void *data available for).

Now, add this as a private element in your class RS232:

class RS232
{

     RS232(string portName, ReceivedCallback dataReceived);
    ~RS232();

private:
    ReceivedCallback dataReceivedCallback;

private:
    static unsigned ReaderThread(void* data);

public:
    SendData(string data);
}

and in the constructor:

RS232::RS232(string portName, ReceivedCallback dataReceived)
{
   ... various stuff to initialize the serial port ... 
   _beginthreadex(securityarg, stacksize, ReaderThread, this, ...)
}

And in the ReaderThread function:

unsigned RS232::ReaderThread(void *data)
{
   RS232 *self = static_cast<RS232*>(data);

   .... stuff to read from serial port ... 

   ... Now call the callback:
   self->dataReceivedCallback(str); 
   .... 
}
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
1

To access the non-static object data in the static thread function you could use this extremely hacky and untested solution. But be warned --- it is for educational purposes only because it is extremely hacky. You should learn about locks and mutexes and probably boost threads. Note, this is a pthread style solution. I've no experience with the function _beginthreadex() but you can see that createThread does the job of creating your thread. Adapt as needed.

typedef int (*ReceivedCallback)(string data);

class RS232
{
public:
    RS232(string portName, ReceivedCallback dataReceived);
    ~RS232();
    SendData(string data);
    createThread();

private:
    ReceivedCallback dataReceivedCallback;
    static unsigned ReaderThread(void* data);
    thread m_someThread;

    struct accessHelper
    {
        RS232* This;
        void *actual_arg;
        accessHelper(RS232 *t, void *p)  
            : This(t), 
              actual_arg(p) 
              {}
    };

};

RS232::createThreaad()
{
     int someData;
     accessHelper ah(this, someData);
     m_someThread.create(RS232::ReaderThread, &ah);
}

RS232::ReaderThread(void *data) 
{
    accessHelper *ah = static_cast<accessHelper*>(data);
    RS232 *This = ah->This;
    This->dataReceivedCallback......
}
Ben J
  • 1,367
  • 2
  • 15
  • 33