-1

I am programming a UART interface (in LINUX) and I want to use a class for that.

The way I have it right now is I have a method for write and one for read. The reading is started in a new thread to get new data, but because of that it has to be static and I don't like that because it needs some attributes from the class like the file descriptor. And methods I call after it finishes reading data have to be static too.

Example (simplified):

class uart {
  public:
    uart(); //initialization
    static void read();
    void write();
  private:
    static void doSomeThingWithData();
}

The doSomeThingWithData() processes the data. I guess I could put this into another class which I would pass with by the constructor.

My question is how do i get rid of the static or is it good style to keep use it? And would it be better to use 2 extra classes for read and write (basically functors) that I use for read and write? Is there a pattern I can use to model my classes here?

  • 2
    Why must your read function be static if you want to do the reading from a new thread? Perhaps you have simplified your code too far, but there are techniques to call member functions from threads. – Bart van Ingen Schenau Dec 13 '19 at 11:31
  • 2
    Can you show the code that creates the thread with sufficient detail for us to understand why you think that requires a static function? – David Schwartz Dec 17 '19 at 5:50
0

I know of two commonly used approaches to deal with this situation.

One uses inheritance, with a static member function that receives a pointer to a class object as a parameter, and calls a virtual function via that pointer:

class BaseUart {
    static void base_read(BaseUart *b) { 
        b->onRead();
    }
public:
    virtual void onRead() = 0;
};

class MyUart : public BaseUart {
     virtual void onRead() override {
         // process some data that's been read
    }
};

The other approach (that's become much more common relatively recently) is to use a lambda expression to specify the code to run in the thread:

class Uart {
public:
    void read() { 
        auto the_thread = std::thread([] { /* code run in thread goes here */ });
    }
};

As to which is preferable between the two: assuming I could, I'd choose the latter nearly every time. The obvious reason to use inheritance instead of a lambda would be that you have no real choice (e.g., you need to support a pre-C++ 11 compiler, so you can't use lambdas).

There are some other details to deal with in threading though. For example, once you create a thread, you usually also want to join it again, and before you do that, you normally need to give it some sort of signal telling it to exit. One common medium for that is an std::atomic<bool>, but it's hard to guess whether that's likely to work well in you case or not.

| improve this answer | |
0

You cannot pass a member function alone to a thread constructor, because the member function requires to know the object on which it has to be executed.

The static function could be a (suboptimal) solution. The trick would then to have a first argument that is a reference to the object:

class uart {
public: 
    ...
    static void static_read(uart& self);
};

...
    thread t1(uart::static_read, ref(u));

But this is not a natural class design. A much better approach would be to write your class independently of any multi-threading consideration and use plain member functions. This would be a sound separation of concerns:

class uart {
public: 
    void init();
    void read();
    ...
};
...

    // as easy as this: 
    thread t3(&uart::read, ref(u)); // don't forget ref() or you'd work on a clone

Finally, you do may opt for a self-standing functor:

// easy way
auto read_my_uart = bind(&uart::read, ref(u));  
thread t4(read_my_uart); 

Or even a class in which you would inject the uart at construction and implement operator() so that you can bass an object of that class to the thread constructor. THis approach would particularly make sense if different threads would use different uart object. The functor object could then manage the uart that it needs.

Here an online demo for experimenting with the different options (and verifying that it's the right object that is invoked).

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.