0

I was getting an error in something called TouchGFX and it was pointed out to me that a virtual function inside a class needed to be defined with curly braces vs just a semicolon at the end of its name. I can do basic C++ but inheritance, virtual functions, etc are not something I use often.

What's the difference between #1 and #2 way to define the virtual function setTimeDate()?

TouchGFX allows you to create "screens" for the display. Like your cellphone display. Turn it on and there is a screen. If you swipe or open an app you get a different screen.

There are two classes that are for all screens (model and modelListener). Each screen in the project gets two more classes (Presenter and a View). I showed below the classes for the principalScreen but there is another ... SecondScreen. There are no virtual functions defined in the SecondScreen ... I haven't gotten to that part of the code yet. The error I was getting was:

./Application/User/gui/SecondScreenPresenter.o:(.rodata._ZTV21SecondScreenPresenter+0x28):undefined reference to `ModelListener::setTime(displayTime_t)'

osMessageQueueGetCount, osMessageQueueGet are part of [CMSIS-RTOS2](https://www.keil.com/pack/doc/CMSIS/RTOS2/html/group__CMSIS__RTOS__Message.html).

I only changed the definition inside the model class and the error went away.

virtual void setTime(displayTime_t time);   // #1 error

virtual void setTime(displayTime_t time){}  // #2 correct
class ModelListener
{
    public:
        ModelListener() : model(0) {}

        virtual ~ModelListener() {}

        void bind(Model* m)
        {
            model = m;
        }

        virtual void setTimeDate(displayTimeDate_t nTimeDate){}

    protected:
        Model* model;

};
Model::Model() : modelListener(0), newTimeDate({0})
{

}

void Model::tick()
{
    if(osMessageQueueGetCount(qRTCtoUIHandle)>0)
    {
        if(osMessageQueueGet(qRTCtoUIHandle, &newTimeDate, 0, 0)==osOK)
        {
            modelListener->setTimeDate(newTimeDate);
        }
    }
}
class principalScreenPresenter : public touchgfx::Presenter, public ModelListener
{
    public:
        principalScreenPresenter(principalScreenView& v);
    
        /**
         * The activate function is called automatically when this screen is "switched in"
         * (ie. made active). Initialization logic can be placed here.
         */
        virtual void activate();
    
        /**
         * The deactivate function is called automatically when this screen is "switched out"
         * (ie. made inactive). Teardown functionality can be placed here.
         */
        virtual void deactivate();
    
        virtual ~principalScreenPresenter() {};
    
        virtual void setTimeDate(displayTimeDate_t nTimeDate);
    
    private:
        principalScreenPresenter();
    
        principalScreenView& view;
};
class principalScreenView : public principalScreenViewBase
{
    public:
        principalScreenView();
        virtual ~principalScreenView() {}
        virtual void setupScreen();
        virtual void tearDownScreen();

        virtual void setTimeDate(displayTimeDate_t nTimeDate);

    protected:

};

I was following a YouTube video to do this, but using a struct instead of an int like in the video, I thought I accidentally copy/pasted the function in a different file. But after a few hours of trying stuff, I discovered that it was related to a screen interaction. I couldn't figure the error out on my own.

I asked in the STM forum and someone gave me the answer. I'm trying to understand more about the solution. My C++ book (very old) talks about virtual functions, but not this small difference. There is lots of stuff online, but I could find an explanation for this.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Rodo
  • 115
  • 3
  • There isn't really anything special about `virtual` here. You can define any member function inside the class definition in this way and you can define the `virtual` function outside the class just like any other function also. `virtual void setTime(displayTime_t time);` is never a definition, just a declaration, regardless of whether or not `virtual` is present. – user17732522 Jun 30 '23 at 23:19
  • 1
    But maybe you want these `virtual` functions to be _pure virtual_ functions, meaning that they should always be overridden by the derived class, so that no definition in the base is required? The syntax for that is `virtual void setTime(displayTime_t time) = 0;` and special to virtual functions. – user17732522 Jun 30 '23 at 23:22

1 Answers1

1

Your problem is not related to virtual functions and is not even specific to C++ (the same applies to C).

What's the difference between #1 and #2 way to define the virtual function setTimeDate?

virtual void setTime(displayTime_t time);   // #1 error

virtual void setTime(displayTime_t time){}  // #2 correct

The difference is that #1 is only a function prototype, not a definition. It assumes the function is defined elsewhere. If you don't provide the function definition anywhere, the linker produces "undefined reference" error.

In #2, you provided a function body{}, which defines an empty (aka no-op) function.

Specifically with virtual functions, you may specify that it is pure, i.e., it does not have any implementation, by adding =0 at the end:

virtual void setTime(displayTime_t time) = 0;

However, if it is included in a class, this makes the class abstract, i.e., you cannot create a variable of this class. It can only serve as a base class.

Eugene
  • 6,194
  • 1
  • 20
  • 31
  • 1
    The `{}` is not a function definition. The entire construct `virtual void setTime(displayTime_t time){}` is the function definition. The `{}` is a *function block* (sometimes described informally as a *function body*) and is an essential component of a function definition. It is also possible (but optional) to define a pure virtual function (e.g. to provide a base class version of the function that can be called by overriding functions in derived classes) and such a definition may be either inline or not. – Peter Jun 30 '23 at 23:34