0

I'm working on a visual debugger for an embedded application running on stm32. So the debugger will run on a PC an reuse the same code as the main application except for the low level functions triggering hardware reaction and will instead send signals for the GUI (QT).

I'm looking for a pattern or something clean that could allow me to do that without big #ifdef in the code.

So to give an example:

I have the gpio.h and gpio.c files playing with the stm32 low level stuffs (they are semi-generated by stmCube so I cannot change them completely).

(C code)
void GPIO_set_realy_state(int relay, bool state)
{
    HAL_GPIO_WritePin(port,relay,state?GPIO_PIN_SETGPIO_PIN_RESET);
}

I have a c++ wrapper above them (GpioWrapper) that is called whenever the application needed to change the state of an IO

GpioWrapper::setRealyState(int relay, bool state)
{
    GPIO_set_realy_state(relay,state);
}

In the PC application, I would like another implementation of that wrapper or something alike that would get called instead of the above one to send a signal instead of calling the low level functions making the GUI to change an icon.

GpioWrapper::setRealyState(int relay, bool state)
{
    emit RelayTriggered(relay,state);
}

The problem that I face is that to send signals, my class need to inherit from QObject and it cannot be the case in GpioWrapper.h given that that part has no clue of the Qt World when used in the embedded application and I would like to avoid #ifdef #else in my wrapper if possible.

What could be the cleaner way to solve that?

  • The general advise is to avoid mixing the GUI and the application algorithms as far as possible. Though if you have a base class in the embedded system, that one could be inherited both by the GPIO driver and the GUI both. In one case it toggles actual I/O pins, in the other case it displays something fancy. – Lundin Oct 03 '16 at 15:20
  • It's exacty what I want to achive, only the low level functions are overriden and all the application logic stay the same. So yes, as @Chajnik-U proposed, I will go with an abstract class implemented in 2 different ways and a static method to retrieve the correct implementation – Bertrand Thelen Oct 04 '16 at 06:44

2 Answers2

0

If you want to avoid the use of QObject, your could implement your uwn SIGNAL/SLOT architecture by using a function callback, in C++(11), use lambda functions.

It's an alternative to implement the notification of callers of certain events.

Many functions of the standard algorithms library use callbacks.

Community
  • 1
  • 1
mohabouje
  • 3,867
  • 2
  • 14
  • 28
  • I do use lambdas when the caller knows what he has to do with a generic method but here it's the other way around, the caller know nothing about what will be triggered by his call to the setRelayState function. So I don't really see how I could use lambdas here. About the callbacks, I never really used them so the link provide a good explanation but again, it seems to be something more usefull for generic methods where the caller provide the actual logic and not the other way around. Am I right? – Bertrand Thelen Oct 04 '16 at 06:37
0

You can make your GpioWrapper class abstract and place it to header file and have two CPP files with implementations for case of STM and QT to include each to corresponding project

file GpioWrapper.h:

class GpioWrapper{
public:
    static GpioWrapper* Create();
    //...
    virtual void setRealyState(int relay, bool state) = 0;
    //...
};

file GpioWrapperSTM.cpp (to be part of STM project):

class GpioWrapperSTM: public GpioWrapper{
public:
    //...
    void setRealyState(int relay, bool state) override
    {
        GPIO_set_realy_state(relay,state);
    }
    //...
};

GpioWrapper* GpioWrapper::Create(){
    return new GpioWrapperSTM();
}

file GpioWrapperQT.cpp (to be part of QT project):

class GpioWrapperQT: public QObject, public GpioWrapper{
public:
    //...
    void setRealyState(int relay, bool state) override
    {
        emit RelayTriggered(relay,state);
    }
    //...
};

GpioWrapper* GpioWrapper::Create(){
    return new GpioWrapperQT();
}

sample usage somewhere in your APP:

std::shared_ptr<GpioWrapper> wrapper = GpioWrapper::Create();
wrapper->setRealyState(10, 20);

Note: actually one does not have to use std::shared_ptr<> or even dynamic allocation via new (here just to illustrate the idea). One can just declare static GpioWrapper* Instance() method that returns poiner to statically allocated instance, etc (but be aware with so called Meyers singleton - for some old compilers it is not thread safe).

Chajnik-U
  • 501
  • 4
  • 8
  • I will try your solution, the static create function was the missing part I was searching for but I will go for a thread_local instance because the "new" hiden in the method is an open door for a memory leak in my opinion. Thanks for the help :) – Bertrand Thelen Oct 04 '16 at 06:33