0

Theoretical Q

I'm having some trouble when using a referenced variable in a class.

Class A have an int a=0. Class B has an instance of Class A, and referencing int &a=A.a, and in the same manner Class C has an instance of Class B and int &a=B.a. Code crashed when trying to update a variable when using C instance.

Relvant Example

I'm creating a library for TFT+Touch 2.4 screen, to handle buttons more easily.

Libraries XPT2046_Touchscreen and Adafruit_ILI9341 are included here, but initiated in main .ino file for easier handling in loop and more control in main code.

1) MessageTFT

A "basic" class was created, called MessageTFT. This class creates the graphics, and stores its geometrical properties and color as shown below.

2) ButtonTFT

ButtonTFT based on MessageTFT adding touch capabilities to it. Its geometrical and color parameters are referenced to MessageTFT. When creating a button instance, user defines its properties but, actually it is referenced to MessageTFT.

ButtonTFT and MessagesTFT works as expected. For example a button is defined in .ino:

ButtonTFT butt;

and defined in void setup():

  butt.a = 200;
  butt.b = 60;
  butt.xc = tft.width() / 2;
  butt.yc = tft.height() - butt.b / 2;
  butt.txt_size = 1;
  butt.face_color = ILI9341_CASET;
  butt.txt_color = ILI9341_DARKGREY;
  butt.border_color = ILI9341_MAGENTA;
  butt.roundRect = false;
  butt.createButton("Press!");

and in void loop():

  if (ts.touched())
  {
    TS_Point p = ts.getPoint();
    if (butt.checkPress(p))
      {
        Serial.print("Pressed");
      }
  }

3) buttonArrayTFT

Evolving to buttonArrayTFT, which spreads up to 8 buttons x and y centered on screen. Here again using same way to reference relevant properties (in buttonArrayTFT each button's size and position are not defined by user, but calculated to optimize size and position). Now variable are referenced to a ButtonTFT instance, for example &txt_size = _button0.txt_size as shown below.

Since txt_size is common for all button members in that array of buttons, it is referenced for all buttons later in code.

Also buttonArrayTFT works as expected, when defining its instance as:

char *a[] = {"All Windows", "Saloon", "Room", "Specific"};
buttonArrayTFT mainWindows;

and created:

  mainWindows.create_array(4, 1, a);

as a 4X1 button array with text for each button as in a.

The Problem (code crashes)

when trying to pass a value for any of referenced parameter ( taking txt_size as an example ):

mainWindows.txt_size = 3;          // <----- This 
mainWindows.create_array(4, 1, a);

While when doing same thing when initiating a button (see up ), I use same way to update button's text size (which is refenced to MessageTFT ).

What may be the reason for that ??

Guy

.h file:

#ifndef TFT_GUI_h
#define TFT_GUI_h

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <XPT2046_Touchscreen.h>

/* Screen calibration */
#define TS_MIN_X 350
#define TS_MIN_Y 350
#define TS_MAX_X 3800
#define TS_MAX_Y 3800
/* ~~~~~~~~~~~~~~~~~ */

/* For Wemos Mini and TFT screen 2.4" */
#define TFT_CS D0
#define TFT_DC D8
#define TFT_RST -1
#define TS_CS D3
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

const uint8_t _pos_corr_factor_x = 3;
const uint8_t _pos_corr_factor_y = 4;

extern XPT2046_Touchscreen ts; /* Touch screen */
extern Adafruit_ILI9341 tft;   /* Graphics */

class MessageTFT
{
public:
  int xc = 0;
  int yc = 0;
  uint8_t a = 0;
  uint8_t b = 0;
  uint8_t txt_size = 3;
  uint8_t border_thickness = 1;
  char txt_buf[20];
  bool roundRect = true;
  uint8_t screen_rotation = 0;
  uint16_t face_color = ILI9341_GREENYELLOW;
  uint16_t txt_color = ILI9341_BLACK;
  uint16_t border_color = ILI9341_RED;
  Adafruit_ILI9341 *TFT[1];

public:
  MessageTFT(Adafruit_ILI9341 &_tft = tft);
  void createMSG(char *txt);
  void clear_screen(uint8_t c = 0);

private:
  void _put_text(char *txt);
  void _drawFace();
  void _drawBorder();
};

class ButtonTFT
{
public:
  /* Following vars belong to MSG class*/
  MessageTFT MSGwindow;
  int &xc = MSGwindow.xc;
  int &yc = MSGwindow.yc;
  uint8_t &a = MSGwindow.a;
  uint8_t &b = MSGwindow.b;
  uint8_t &txt_size = MSGwindow.txt_size;
  uint8_t &screen_rotation = MSGwindow.screen_rotation;
  uint16_t &face_color = MSGwindow.face_color;
  uint16_t &txt_color = MSGwindow.txt_color;
  uint16_t &border_color = MSGwindow.border_color;
  bool &roundRect = MSGwindow.roundRect;
  char *txt_buf = MSGwindow.txt_buf;
  /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

  XPT2046_Touchscreen *TS[1];

public:
  ButtonTFT(XPT2046_Touchscreen &_ts = ts, Adafruit_ILI9341 &_tft = tft);
  void createButton(char *txt);
  bool wait4press();
  bool checkPress(TS_Point &p);

private:
  int _tft_x, _tft_y;

private:
  void _put_text();
  void _press_cb();
  /* fucntions below converts touchscreen values to TFT coordinates */
  void _conv_ts_tft(TS_Point &p);
  bool _check_press_geometry(TS_Point &p);
  int _TS2TFT_x(int px);
  int _TS2TFT_y(int py);
};


class buttonArrayTFT
{
public:
  buttonArrayTFT(XPT2046_Touchscreen &_ts = ts, Adafruit_ILI9341 &_tft = tft);
  void create_array(uint8_t R, uint8_t C, char *but_txt[]);
  uint8_t checkPress(TS_Point &p);

public:
  uint8_t &txt_size = _button0.txt_size;
  uint16_t &txt_color = _button0.txt_color;
  uint16_t &border_color = _button0.border_color;
  bool &roundRect = _button0.roundRect;
  int shift_array = 0;

private:
  ButtonTFT _button0;
  ButtonTFT _button1;
  ButtonTFT _button2;
  ButtonTFT _button3;
  ButtonTFT _button4;
  ButtonTFT _button5;
  ButtonTFT _button6;
  ButtonTFT _button7;
  ButtonTFT *_buttons[8] = {&_button0, &_button1, &_button2, &_button3,
                            &_button4, &_button5, &_button6, &_button7};

private:
  uint8_t _num_items = 0;
};


#endif

Edit1: How Constructors are defined in .cpp file

MessageTFT::MessageTFT(Adafruit_ILI9341 &_tft)
{
  TFT[0] = &_tft; // <--- stored in a pointer
}

and

ButtonTFT::ButtonTFT(XPT2046_Touchscreen &_ts, Adafruit_ILI9341 &_tft) : MSGwindow(_tft)
{
  MSGwindow.TFT[0] = &_tft;
  TS[0] = &_ts;
}

and

buttonArrayTFT::buttonArrayTFT(XPT2046_Touchscreen &_ts, Adafruit_ILI9341 &_tft)
    : _button0(_ts, _tft), _button1(_ts, _tft), _button2(_ts, _tft),
      _button3(_ts, _tft), _button4(_ts, _tft), _button5(_ts, _tft),
      _button6(_ts, _tft), _button7(_ts, _tft)
{
  _button0.MSGwindow.TFT[0] = &_tft;
  _button0.TS[0] = &_ts;
}
3
  • 1
    Sounds like you should actually be using inheritance and polymorphism for this task.
    – Majenko
    Oct 14 at 9:28
  • 3
    For example in my display library twButton inherits from Widget which itself is a form of Image that is then a virtual Cariad display device.
    – Majenko
    Oct 14 at 9:31
  • @Majenko can you please tell what is wrong in my code?
    – guyd
    Oct 14 at 9:47
3

I'm not sure what value this is going to have for Arduino users, but here goes. Your question seem to be an over-complicated form of this the following:

My code:

struct MessageTFT {uint8_t txt_size = 3;};

struct ButtonTFT {
  MessageTFT MSGwindow;
  uint8_t &txt_size = MSGwindow.txt_size;
};

struct buttonArrayTFT {
  uint8_t &txt_size = _button0.txt_size;
  ButtonTFT _button0;
};

buttonArrayTFT bat;

void setup() {
  Serial.begin(9600);
  Serial.println(F("\n\n\nStart."));
}

void loop() {
  Serial.println(bat.txt_size++, DEC);
  delay(1000);
}

My question:

Why does this crash? ...or not print the correct value? or whatever.

I don't think I'm straw-manning (for lack of better term) your question. But, the question is kind of a mess, so this a best-effort response to it. If I've clarified the question to the point of being off-topic, so be it. If I do have the correct distillation, if nothing else what people can get out of this is that your questions here can usually be made simpler.

The declared order of members in a class is relevant to the order of initialization; there's more to it than just that. In any case, _button0 has not been initialized prior to your uint8_t &txt_size = _button0.txt_size; The mechanism underlying a member reference in a class is basically that of a pointer. You are essentially copying an uninitialized pointer out of _button0 and into the txt_size member of buttonArrayTFT; that is the uint8_t &txt_size = MSGwindow.txt_size has not yet been executed.

You can fix this by doing nothing more than reordering the members like so:

struct buttonArrayTFT {
  ButtonTFT _button0;
  uint8_t &txt_size = _button0.txt_size;
};

Now uint8_t &txt_size = MSGwindow.txt_size; in ButtonTFT executes prior to uint8_t &txt_size = _button0.txt_size; in buttonArrayTFT.

This will fix the problem as I've presented. Should you do this? I'm not sure. But then that's not what was asked anyway. Still, you may be better served by getting rid reference members or converting them to inline functions that return a reference type. Or any number of other strategies.

At a minimum this is a problem your code has.

6
  • 1
    Re “You are essentially copying an uninitialized pointer”: My understanding is that, even though _button0 has not been initialized at this point, it has already been allocated. He is thus copying a pointer to uninitialized data, not an uninitialized pointer. Did I miss something? Oct 14 at 13:18
  • 1
    The mechanism underlying uint8_t &txt_size in ButtonTFT is roughly speaking a pointer. When the reference in ButtonTFT is bound to the txt_size field in MSGwindow, it is initing two bytes of ButtonTFT with the address of the field in MSGwindow. Those two bytes (which are basically a pointer) have not be initialized prior to uint8_t &txt_size = _button0.txt_size; The uint8_t &txt_size = _button0.txt_size; is binding the txt_size reference of buttonArrayTFT to its counterpart in the ButtonTFT member.This binding is effectively the copying of an (unitialized) pointer.
    – timemage
    Oct 14 at 13:27
  • I'm not sure I do it justice in a comment, I mangled the above pretty bad trying to make it fit. If you want you can find me on libera.chat and I'll explain there. Or spend some time with it and if it's still not making sense, I will try to update the answer later. The "pointers" I'm speaking of are only to illustrate what's happening underneath the references in terms of something that may already be understood.
    – timemage
    Oct 14 at 13:41
  • 1
    Now it is clearer in my mind, thanks! Oct 14 at 13:44
  • 1
    In buttonArrayTFT you have uint8_t &txt_size = _button0.txt_size; and seven lines later you have: ButtonTFT _button0; If that is in your code as you've shown it, irrespective of what else is in your code, then what I've said the answer applies.
    – timemage
    Oct 14 at 14:04

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