How to define a virtual method in a class

Based on a previous suggestion from someone in this forum, I am trying to figure how to define a virtual method inside a class.

I tried multiple ways of defining it but each one generates some kind of error. Below you will find the latest attempt and the error message.

The Error message

Arduino: 1.8.12 (Windows 10), TD: 1.53, Board: "Arduino Uno"

C:\Users\Mike\Desktop\MRE\MRE.ino: In constructor 'CreateButton::CreateButton(String, int, int, int, String, int, int)':

MRE:75:36: error: qualified-id in declaration before '(' token

virtual void CreateButton::Action(void)

^

Multiple libraries were found for "Adafruit_HX8357.h"
Used: C:\Users\Mike\Documents\Arduino\libraries\Adafruit_HX8357_Library
Not used: C:\Users\Mike\Documents\Arduino\libraries\Adafruit_HX8357_Library-1.1.8
exit status 1
qualified-id in declaration before '(' token

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

#include <Adafruit_HX8357.h>

#define TFT_RST -1  // dont use a reset pin, tie to arduino RST if you like
#define TFT_DC 9
#define TFT_CS 10

Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST);

// Colors
#define BACKGROUNDCLR 0xB5B6
#define HIGHLIGHTCLR 0x0500
#define TEXTCLR 0x0000
#define BORDERCLR 0x0000
#define BUTTONCLR 0x7D75
#define REDCLR 0xA800
#define GREENCLR 0x0360

/**********************************************************************************/
void setup()
{
  Serial.begin(115200);
}

/**********************************************************************************/
void loop()
{
  
}

/**********************************************************************************/
class CreateButton
{
  private:
    String _key;
    int _txtSize;
    int _x;
    int _y;
    String _txt;
    int _bgClr;
    int _txtClr;

  public:
    CreateButton(String key, int txtSize, int x, int y, String txt, int bgClr, int txtClr);
    virtual void CreateButton::Action(void);
};

/**********************************************************************************/
CreateButton::CreateButton(String key, int txtSize, int x, int y, String txt, int bgClr, int txtClr)
{
  _key = key;
  _txtSize = txtSize;
  _x = x;
  _y = y;
  _txt = txt;
  _bgClr = bgClr;
  _txtClr = txtClr;
  float charWidth[6]  = {6, 6, 12, 18, 24, 28};
  float charHeight[6] = {7, 7, 14, 22, 30, 36};
  float rectWidth, rectHeight;

  rectWidth = (txt.length() + 1) * charWidth[txtSize];
  rectHeight = charHeight[txtSize] + 20;

  tft.drawRect(x, y, rectWidth, rectHeight,  TEXTCLR);
  tft.fillRect(x + 1, y + 1, rectWidth - 2, rectHeight - 2, bgClr);

  int txtStartX = x + (charWidth[txtSize] / 2) + 1;                         // Center the text;
  int txtStartY = y + 10;                                                   // text in the middle

  tft.setCursor(txtStartX, txtStartY);
  tft.setTextColor(txtClr);
  tft.setTextSize(txtSize);
  tft.print(txt);

  virtual void CreateButton::Action(void)
  {
    
  }
}

When declaring your class members, you just name them such as 'Action', not prefixed with the class name (since you are already inside the definition of the class)

You also did not close out your CreateButton::CreatButton() definition with a closing '}'

#include <Adafruit_HX8357.h>

#define TFT_RST -1  // dont use a reset pin, tie to arduino RST if you like
#define TFT_DC 9
#define TFT_CS 10

Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST);

// Colors
#define BACKGROUNDCLR 0xB5B6
#define HIGHLIGHTCLR 0x0500
#define TEXTCLR 0x0000
#define BORDERCLR 0x0000
#define BUTTONCLR 0x7D75
#define REDCLR 0xA800
#define GREENCLR 0x0360

/**********************************************************************************/
void setup()
{
  Serial.begin(115200);
}

/**********************************************************************************/
void loop()
{

}

/**********************************************************************************/
class CreateButton
{
  private:
    String _key;
    int _txtSize;
    int _x;
    int _y;
    String _txt;
    int _bgClr;
    int _txtClr;

  public:
    CreateButton(String key, int txtSize, int x, int y, String txt, int bgClr, int txtClr);
    virtual void Action(void);
};

/**********************************************************************************/
CreateButton::CreateButton(String key, int txtSize, int x, int y, String txt, int bgClr, int txtClr)
{
  _key = key;
  _txtSize = txtSize;
  _x = x;
  _y = y;
  _txt = txt;
  _bgClr = bgClr;
  _txtClr = txtClr;
  float charWidth[6]  = {6, 6, 12, 18, 24, 28};
  float charHeight[6] = {7, 7, 14, 22, 30, 36};
  float rectWidth, rectHeight;

  rectWidth = (txt.length() + 1) * charWidth[txtSize];
  rectHeight = charHeight[txtSize] + 20;

  tft.drawRect(x, y, rectWidth, rectHeight,  TEXTCLR);
  tft.fillRect(x + 1, y + 1, rectWidth - 2, rectHeight - 2, bgClr);

  int txtStartX = x + (charWidth[txtSize] / 2) + 1;                         // Center the text;
  int txtStartY = y + 10;                                                   // text in the middle

  tft.setCursor(txtStartX, txtStartY);
  tft.setTextColor(txtClr);
  tft.setTextSize(txtSize);
  tft.print(txt);
}

void CreateButton::Action(void)
{

}

Constructing and setup are best separated. You've smashed the two together in your constructor.

The constructor and Action are two methods...

CreateButton::CreateButton(String key, int txtSize, int x, int y, String txt, int bgClr, int txtClr)
{
  // construction goes here
}

void CreateButton::Action(void)
{
  // action stuff goes here
}

Thank you both. This version now compiles fine.

#include <Adafruit_HX8357.h>

#define TFT_RST -1  // dont use a reset pin, tie to arduino RST if you like
#define TFT_DC 9
#define TFT_CS 10

Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST);

// Colors
#define BACKGROUNDCLR 0xB5B6
#define HIGHLIGHTCLR 0x0500
#define TEXTCLR 0x0000
#define BORDERCLR 0x0000
#define BUTTONCLR 0x7D75
#define REDCLR 0xA800
#define GREENCLR 0x0360

/**********************************************************************************/
void setup()
{
  Serial.begin(115200);
}

/**********************************************************************************/
void loop()
{

}

/**********************************************************************************/
class CreateButton
{
  private:
    String _key;
    int _txtSize;
    int _x;
    int _y;
    String _txt;
    int _bgClr;
    int _txtClr;

  public:
    CreateButton(String key, int txtSize, int x, int y, String txt, int bgClr, int txtClr);
    virtual void Action(void);
};

/**********************************************************************************/
CreateButton::CreateButton(String key, int txtSize, int x, int y, String txt, int bgClr, int txtClr)
{
  _key = key;
  _txtSize = txtSize;
  _x = x;
  _y = y;
  _txt = txt;
  _bgClr = bgClr;
  _txtClr = txtClr;
  float charWidth[6]  = {6, 6, 12, 18, 24, 28};
  float charHeight[6] = {7, 7, 14, 22, 30, 36};
  float rectWidth, rectHeight;

  rectWidth = (txt.length() + 1) * charWidth[txtSize];
  rectHeight = charHeight[txtSize] + 20;

  tft.drawRect(x, y, rectWidth, rectHeight,  TEXTCLR);
  tft.fillRect(x + 1, y + 1, rectWidth - 2, rectHeight - 2, bgClr);

  int txtStartX = x + (charWidth[txtSize] / 2) + 1;                         // Center the text;
  int txtStartY = y + 10;                                                   // text in the middle

  tft.setCursor(txtStartX, txtStartY);
  tft.setTextColor(txtClr);
  tft.setTextSize(txtSize);
  tft.print(txt);
}

void CreateButton::Action(void)
{
}

Couple ideas for you to make things more clear later.

name the class something like baseButton then your constructor would be

baseButton(all them things passed in to create it);

Then your functionX button would be..

class functionX : public baseButton {

all the things that make functionX unique

virtual Action(void);
};

Also, all those passed in parameters. Are they all necessary? Could you default some? Like textSize perhaps?

-jim lee

Thanks for that suggestion. I'll implement it.

Also, The display uses different text sizes. I could default the text color and background colors but then I would need methods to change them when needed. That might be a good tradeoff.

Another thing I like is to set defaults for items that for the most part work fine one way, and only sometimes need to be different.

For example..

CreateButton(String key, int txtSize, int x, int y, String txt, int bgClr, int txtClr)

Lets say these are all things we need to input. But some are almost always the same.

We can set defaults in the .h file for the ones that are typically the same value.

CreateButton(String key, int x, int y, String txt, int txtSize=1, int bgClr=WHITE_COLOR, int txtClr=BLACK_COLOR);

So, when Mrs user wants to create a button they only need to type...

CreateButton myResetButton("MyKey", xPos,yPos,"Reset"); // And the rest will be filled with the defaults.

This is really handy for stuff that the typical user doesn't need or want to deal with. (Including yourself) But it leaves open the ability for someone who really needs to set something different (Including yourself) to do just that.

Hope this helps.

-jim lee

Interesting, I was under the impression only the last argument could be optional. This is nice.

The last n arguments.

-jim lee

ALL arguments can be optional, but if you want to provide any one argument, you MUST provide ALL arguments preceding that one as well.

Regards,
Ray L.

Outstanding. Implementing that now.

Thanks!