Go Down

Topic: Template value class inheriting name from base (Read 184 times) previous topic - next topic

syphex

#15
May 12, 2019, 04:59 pm Last Edit: May 12, 2019, 05:14 pm by syphex
the class has only the notion of a pointer to pointer of Menu. it does not know how many rows or lines you have, this information is not kept in the array definition. it's for you to know. that's why you can't even know how many elements you have in the array and in the setup I have
Code: [Select]
  topMenu.nbEntries = sizeof(M0) / sizeof(M0[0]);

a good design pattern would be to separate the presentation layer from the abstraction. So store the data as a flat list and then either within the class or as a separate class, attach information about the layout.
I understand that but the layout is so straight forward it really doesn't seem worth implementing separately.

Menu*** menulist would be an array of an array of Menu pointers? Its a 2D array of Menu pointers so it should work.

The first layer tells you the number of lines, and the next layer is the number of columns.

Code: [Select]

int cols = sizeof(menulist) / sizeof(menulist[0]);
int rows = sizeof(menulist[0]) / sizeof(menulist[0][0]);

Or something.

I just don't understand why I can do:

Code: [Select]

SET_TIME.menulist[0][1]=&set_hour; //menulist[0][0] reserved for previous Menu
  SET_TIME.menulist[0][2]=&set_minute;
  SET_TIME.menulist[0][3]=&set_second;
  SET_TIME.menulist[1][0]=&set_day;
  SET_TIME.menulist[1][1]=&set_month;
  SET_TIME.menulist[1][2]=&set_year;


But cant do

Code: [Select]

SET_TIME.menulist = {{&set_hour, &set_minute, &set_second},{&set_year, &set_month, &set_day}};

J-M-L

#16
May 12, 2019, 05:28 pm Last Edit: May 12, 2019, 05:32 pm by J-M-L
the notion of 1D or 2D or 3D arrays is just an abstraction.  In reality the compiler does not store the dimensions anywhere, you just have consecutively in memory some data

for example
Code: [Select]
byte myArray[2][3] {{1,2,3}, {4,5,6}}; will just result in storing consecutively in memory | 1 | 2 | 3 | 4 | 5 | 6 |

As long as you access the data using the variable name myArray, the compilers knows the dimensions and will do the right thing

but if you do
Code: [Select]
byte* arrayPtr = (byte*) myArray; then the dimensions are lost as the compiler does not know how many elements are pointed. it's for you to know.

for example If I use that code
Code: [Select]
byte myArray[2][3] = {{1, 2, 3}, {4, 5, 6}};

byte* arrayPtr = (byte*) &myArray;

void setup()
{
  Serial.begin(115200);

  Serial.println("--- SIMPLE 3x2 ARRAY DUMP ---");
  for (byte r = 0; r < 2; r++) {
    for (byte c = 0; c < 3; c++) {
      Serial.print(myArray[r][c]);
      Serial.print(" ");
    }
    Serial.println();
  }
  Serial.println();
  Serial.println("--- MEMORY DUMP 6x1 ---");

  for (byte i = 0; i < 6; i++) { // I know I've 6 elements
    Serial.print(*(arrayPtr+i)); // just printing the data in consecutive memory slots
    Serial.print(" ");
  }
  Serial.println();
  Serial.println();
 
  Serial.println("--- MEMORY DUMP ORGANIZED THE WAY I WANT 2x3 ---");
  for (byte r = 0; r < 3; r++) {
    for (byte c = 0; c < 2; c++) {
      Serial.print(*(arrayPtr + 2*r+c));
      Serial.print(" ");
    }
    Serial.println();
  }
}

void loop() {}

the console will show


--- SIMPLE 3x2 ARRAY DUMP ---
1 2 3
4 5 6

--- MEMORY DUMP 6x1 ---
1 2 3 4 5 6

--- MEMORY DUMP ORGANIZED THE WAY I WANT 2x3 ---
1 2
3 4
5 6



first print is by iterating through rows and columns.
but if I keep only a pointer to the start of the memory, you can see that by just going through the memory consecutively I can find and print my 6 values in a line. (6x1)
in the last example, I present the same data in 3 lines of 2 columns

--> so it's just a matter of deciding how you want to show the information

==> as your menu can hold a variable number of entries (and once you allocate the array to the instance variable in your class you lose the info and you only keep a pointer to the start), unless you store somewhere the dimension of the array or the layout you want, you'll be stuck


 
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

syphex

#17
Today at 05:41 am Last Edit: Today at 05:44 am by syphex
I have decided on the following approach, thank you for your contribution to my understanding of arrays, please now help me with the topic at hand. I am trying to print the names and values to an LCD screen. Below is my code just trying to print 2 lines manually for debugging purposes. As you can see I am trying to print the value inside a ValueItem object which is being pointed to by a MenuItem* array. Since the base class doesn't know about Value, I am trying dynamic_cast. But I am getting a 'dynamic_cast' not permitted with -fno-rtti error, even though MenuItem is PolyMorphic.

Code: [Select]

#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>

class  MenuItem
{
public:
char Name[15];
//void* value;

virtual GetValue(){}

MenuItem(char* MyName){
strcpy(Name, MyName);
}
};

template <byte x, byte y>class Menu:MenuItem
{
  public:
  MenuItem* MenuList[x][y]={{0}};
  Menu(const char* MyName):MenuItem(Name){
    strcpy(Name, MyName);
   
  }
};

template <class T> class ValueItem: public MenuItem
{
    public:
    T value;
    const char* UnitStr;
    ValueItem(const char* MyName="",const char* Unit="", T Value=0): MenuItem(MyName)
    {
      value=Value;
      UnitStr=Unit;   
    }

     GetValue() // Cant use T GetValue() :(
    {
      return (T)value;
    }
};

  Menu<1,2> Main("Main: ");
  MenuItem Label_Temperature("Air Temp");
  ValueItem<float> Temperature("Temp: ","\xDF\C",24.6);

// The shield uses the I2C SCL and SDA pins.
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();



void setup() {
  // Debugging output
  Serial.begin(9600);
  // put your setup code here, to run once:

   Main.MenuList[0][0]=&Label_Temperature;
   Main.MenuList[0][1]=&Temperature;
   Serial.println(Label_Temperature.Name);
   Serial.print(Temperature.Name);
   Serial.print(Temperature.value);
   Serial.print(Temperature.UnitStr);

  lcd.begin(16, 2);

  lcd.print("Hello, world!");
  delay(1000);
  lcd.clear();
  lcd.print(Main.MenuList[0][0]->Name);
  lcd.setCursor(0, 1);
  lcd.print(Main.MenuList[0][1]->Name);
  //lcd.print(Temperature.value);              //works
  lcd.print((dynamic_cast<ValueItem<float>*>(Main.MenuList[0][1]))->GetValue()); //does not work
  lcd.print(Temperature.UnitStr);
}

uint8_t i=0;
void loop() {
}


The problem is that if I try to GetValue() without dynamic cast it is treated as an integer. Also GetValue() has no return type, because if I try T GetValue() then I get "conflicting return type specified for 'T ValueItem<T>::GetValue() [with T = float]'" error, which makes no sense to me as the return type is clearly specified as type T?

Go Up