Usare template con array di oggetti

Ciao a tutti, questo è la mia prima richiesta di aiuto sul forum, spero di non fare casino con l'erditor del messaggio..
Ad ogni modo il mio cruccio è questo, ho definito una classe che dovrebbe gestire le voci di un menù da visualizzare in un LCD 2x16 e comandato da 4 comandi base (uo, down, esc, enter). Ho già un prototipo funzionante ma è limitato dal fatto che non riesco a caricare array con dimensioni variabili in una oggetto. Dopo aver studiato un po di C++ ho trovato un modo per farlo su un singolo oggetto, tuttavia implementarlo in un array di oggetti no mi riesce e viene generato il seguente errore di compilazione:

/tmp/cc5qVNez.ltrans0.ltrans.o: In function `main':
/app/Arduino/hardware/arduino/avr/cores/arduino/main.cpp:43: undefined reference to `setup'
/app/Arduino/hardware/arduino/avr/cores/arduino/main.cpp:46: undefined reference to `loop'
collect2: error: ld returned 1 exit status
exit status 1
Errore durante la compilazione per la scheda Arduino Uno.

l'errore viene generato nella chiamata dei metodi all'interno del loop (nol loop non sono richieste le variabili definite nel template). Riporto il codice dello sketch che ho scritto per testare gli oggetti definiti dalla classe:

#include "MenuLCD.h"
#define LCD_H 2 // LCD's number of rows
#define LCD_W 16  // LCD's number of char

template< typename T, byte R, byte C >
MenuLCD<T, R, C> *menu[4];

char cmd = "";
byte menu_pos = 0;
/* 
 * array for the variables associated to the menu: 
 * indexes[][0]: are the values - required
 * indexes[][1]: should be the bottom limits of the range - required
 * indexes[][2]: should be the top limits of the range - required
 * indexes[][3]: should be the incremental step in changing values - optional
 * if not specified is set to 1/20 of range
 */
float values[][4] = {
  {0.0, 0.0, 5.0, 1.0},  
  {0.0, 0.0, 60.0, 2.0},
  {0.0, -1.0, 1.0, 0.1},
  {0.0, -2.0, 2.0, 1.0}, 
  {0.0, 0.0, 1.0, 0.1},
  {0.0, 0.0, 100.0, 5.0},
  {0.0, 0.0, 1.0, 1.0}  // associated to the confirmation on first item in menu[3]
};
byte val_ref = 0 ;

template< typename T, byte R, byte C >
void setup() {
  Serial.begin(9600);
/* 
 * Constructors: 
 * the first value is the LCD's rows (byte). - needed
 * the second value is the LCD's chars per line (byte). - needed
 * the third value is the menu references number (byte). - needed
 * the fourth value is the reference snumber to the previous menu (byte). - needed
 */
  String m0l[][LCD_W]= {"primo", "secondo", "terzo"};
  String m1l[][LCD_W]= {"alfa", "beta", "gamma"};
  String m2l[][LCD_W]= {"Qui", "Quo", "Qua"};
  String m3l[][LCD_W]= {"Tizio", "Caio"};
 
  menu<T, R, C>[0] = new MenuLCD<T, R, C> (2, LCD_W, 0, 0);
  menu<T, R, C>[1] = new MenuLCD<T, R, C> (2, LCD_W, 1, 0);
  menu<T, R, C>[2] = new MenuLCD<T, R, C> (2, LCD_W, 2, 0);
  menu<T, R, C>[3] = new MenuLCD<T, R, C> (2, LCD_W, 3, 2);

// List of strings to display in each menu 
  menu<String, 3, LCD_W>[0] -> set_list(m0l);
  menu<String, 3, LCD_W>[1] -> set_list(m1l);
  menu<String, 3, LCD_W>[2] -> set_list(m2l);
  menu<String, 2, LCD_W>[3] -> set_list(m3l);
/*  
 * Reference to the next menu for each elemento of a menu. Insert 255 to set a variable setting 
 * or to a dead end. Insert a value of 254 to confirm an action (a variable in a 0-1 range is request).
 */
  menu<T, R, C>[0] -> set_next(1, 2, 255);
  menu<T, R, C>[1] -> set_next(255, 255, 255);
  menu<T, R, C>[2] -> set_next(255, 3, 255);
  menu<T, R, C>[3] -> set_next(254, 255);  
/*  
 * Reference to the variables in the values array (for each elemento of a menu). 
 * Insert 255 to lead to a new menu or dead end.
 */
  menu<T, R, C>[0] -> set_val_ref(255, 255, 0);
  menu<T, R, C>[1] -> set_val_ref(1, 2, 3);
  menu<T, R, C>[2] -> set_val_ref(4, 255, 255);
  menu<T, R, C>[3] -> set_val_ref(6, 5);
/*
 * ser user defined strings displayed during the confirm and change value mode
 */
  menu<T, R, C>[0] -> set_conf_str(F("Confermi ?"), F("Sì"), F("No"), F("SAME"));
  menu<T, R, C>[1] -> set_conf_str(F("Confermi ?"), F("Sì"), F("No"), F("SAME"));
  menu<T, R, C>[2] -> set_conf_str(F("Confermi ?"), F("Sì"), F("No"), F("SAME"));
  menu<T, R, C>[3] -> set_conf_str(F("Confermi ?"), F("Sì"), F("No"), F("SAME"));

/*  
 * print the output strings on serial terminal (for debugging)   
 */  
  menu<T, R, C>[0] -> serial_on();
  menu<T, R, C>[1] -> serial_on();
  menu<T, R, C>[2] -> serial_on();
  menu<T, R, C>[3] -> serial_on();

  Serial.println("OK");
  Serial.println("Press 'u' + enter to go up the menu");
  Serial.println("Press 'j' + enter to go down the menu");
  Serial.println("Press the space bar + enter to enter a menu or confirm"); 
  Serial.println("Press the space 'q' +  enter to go esc"); 
}

template< typename T, byte R, byte C >
void loop() {
  if (Serial.available()) {
    cmd = Serial.read();
    Serial.println(cmd);
  }

  switch (cmd) {
  case 'u':
    menu<T, R, C>[menu_pos] -> up();
    menu<T, R, C>[menu_pos] -> get_line(0);
    menu<T, R, C>[menu_pos] -> get_line(1);
    cmd = "";
    break;
  case 'j':
    menu<T, R, C>[menu_pos] -> down();
    menu<T, R, C>[menu_pos] -> get_line(0);
    menu<T, R, C>[menu_pos] -> get_line(1);
    cmd = "";
    break;
  case ' ':
    val_ref = menu<T, R, C>[menu_pos] -> value_ref();
    menu<T, R, C>[menu_pos] -> enter(&menu_pos, &values[val_ref][0], values[val_ref][1], values[val_ref][2], values[val_ref][3]);
    menu<T, R, C>[menu_pos] -> get_line(0);
    menu<T, R, C>[menu_pos] -> get_line(1);
    cmd = "";
    break;
  case 'q':
    menu<T, R, C>[menu_pos] -> esc(&menu_pos);
    menu<T, R, C>[menu_pos] -> get_line(0);
    menu<T, R, C>[menu_pos] -> get_line(1);
    cmd = "";
    break;
  }
}

Qualcuno sa dirmi se è un problema di compatibilità tra il C++ standard e il C di Arduino? tanto per capire se abbandonare l'impresa o meno.

Grazie in anticipo. :slight_smile:

Ok, mi son reso conto che il codice del programma del primo post è troppo pesante,, quindi ho riprodotto l'effetto in un codice minimale commentato per spiegare i passaggi. Il messaggio di errore è il medesimo. Posto quindi il sia il codice principale che il modulo con la classe. La classe dovrebbe andar bene definita così, credo che l'inghippo stia nel codice principale.

Il corpo principale:

#include "ChrList.h"  // includo il modulo della classe

template <typename T, int R, int C>  // specifico un template per un array bidimensionale di un qualche tipo
ChrList<T, R, C>  * ListObj[2];  // creo un arrai di puntatori del tipo ChrList<>

// definisco due array di caratteri
char list1[][20] = {"first", "second", "third", "fourth"};
char list2[][20] = {"Chip", "Dale"};


template <typename T, int R, int C> // specifico un template come sopra
void setup() {
 // inizzializzo la seriale
 Serial.begin(9600);
 // creo due oggetti tipo Chrlist con relative variabili template e li inizializzo ciascuno con un diverso array
 ListObj<T, R, C>[0] = new ChrList<T, R, C> (list1);
 ListObj<T, R, C>[1] = new ChrList<T, R, C> (list2);


//  ChrList<T, R, C> ListObj = ChrList<T, R, C>(list1);
//  ChrList<T, R, C> ListObj = ChrList<T, R, C>(list1);
 // richiamo i metodi print_list per stampare il contenuto delle liste immagazzinae negli oggetti  
 ListObj<T, R, C>[0]-> print_list();   
 ListObj<T, R, C>[1]-> print_list();   

}

template <typename T, int R, int C >
void loop() {}

e qui la definizione della classe:

#ifndef Chr_list_h
#define Chr_list_h

template <typename T, int R, int C >  // specifico un template come nel corpo principale per la classe Chrlist
class ChrList {
  // definsco alcune variabili private tra cui un array definito dal template
  private:
    T *list[R][C];
    int rows;
    int cols;
      
  public:
    ChrList(const T (&Array)[R][C]); // inizzializzo il costruttore
    void print_list(); // inizzializzo il metodo print_list
};

template <typename T, int R, int C>  // specifico un template
ChrList<T, R, C>::ChrList(const T (&Array)[R][C]) {
  // salvo le dimensioni dell'array in rows e cols
  rows = R;
  cols = C;
  // copio il contenuto della lista passata al costruttore (Array) nella lista privata (list)
  for (int r = 0; r < R; r++){
    for (int c = 0; c < C; c++){
      list[r][c] = Array[r][c];
    }
  }
}

template <typename T, int R, int C>  // specifico un template
void ChrList<T, R, C>::print_list(){
  // stampo su seriale il contenuto della lista privata (list)
  for (int r = 0; r < rows; r++){
      Serial.println(list[r]);
    }
}

#endif

Ripeto che l'errore nasce quando cerco di usare i template con array di oggetti mentre per oggetti definiti singolarmente si riescono ad importare array di tipo e dimensioni variabile in un oggetto. Se qualcuno ha già affrontato problematiche del genere sarò grato per qualsiasi delucidazione su come usare i template nelle classi. Va bene anche se qualcuno mi dice che la strada che ho intrapreso è impraticabile per il compilatore di Arduino.