Richiamare un metodo globale da una classe

Salve,
sono abbastanza alle prime armi con il c++ e mi sto scontrando con un problema di risoluzione del nome di una funzione in una classe. Ho scritto un codice per fare una serie di menu su LCD gerstiti da un encoder. per fare questo ho scritto una classe myMenu ( forse non molto ottimizzata ) con costruttore e metodi che funziona bene. purtroppo essendo i menù tanti l'occupazione della RAM è considerevole e alla fine arrivo a saturarla. Pensando di liberarla pensavo di utiliz zare la funzione pgm_read_word ( per salvare in flash tutte le stringhe costanti della classe ) che però mi funziona solo nello spazio globale.
In pratica ho questa situazione:

         #include	    "myMenu.h"

	 #define BLANKLINE "                    "
	 #define STARLINE  "********************"
	 #define HEADER1   " CPA2 Temp. C Contr."
                                     ........
	 #define MENU24    " Kp __________00.00 "
	 #define MENU25    " Ki __________00.00 "
	 #define MENU26    " Kd __________00.00 "

	const char blankLine  [21]  PROGMEM = BLANKLINE;   //    0
	const char starLine   [21]  PROGMEM = STARLINE ;   //    1
	const char header1    [21]  PROGMEM = HEADER1  ;   //    2
                                          .................
	const char menu24	    [21]  PROGMEM = MENU24	 ;   //   27
	const char menu25     [21]  PROGMEM = MENU25	 ;   //   28
	const  char menu26	    [21]  PROGMEM = MENU26	 ;   //   29

	 const char * const string_table[30] PROGMEM =
  {
      blankLine,  //  0
      starLine,   //  1
      header1,
         ..............
      menu24,
      menu25,
      menu26    //  30

  };

char myBuffer[21] ;

	void myLcdPrint ( int8_t index ) 
	{
    strcpy_P(myBuffer, (char*)pgm_read_word(&( string_table[  index ]))); 
   // Serial.println( buffer );
    cpa2Lcd -> print( myBuffer );
	return ;
        }

void setup ()
{
...........
menu 		  = 	new myMenu 	(.......)
.....
}
void loop ()
{
......
}

// in myMenu.h
{
public :
.........
private :

void  Line_write                      ( uint8_t  , uint8_t );

};

// in mymenu.cpp

void myMenu::Line_write ( uint8_t rowNumber ,uint8_t dum_c )
{   
    cpa2Lcd -> home();
    cpa2Lcd -> setCursor ( 0 , rowNumber );
	::myLcdPrint ( dum_c) ;
  return ;
}

In pratica sia che io metta o non metta l'operatore di di risoluzione dell'ambito :: al momento della compilazione ottengo sempre lerrore :
error: '::myLcdPrint' was not declared in this scope ::myLcdPrint ( dum_c) ;

L'errore si presenta sia che io usi come IDE platformio sia che usi arduino IDE 2.1.0

Sono fermo e non riesco a capire dove sto sbagliando .......
Oltretutto è l'unico errore di tutto il codice che è abbastanza articolato.....
Sarò molto grato per qualsiasi aiuto potrete darmi .......

Ci ho messo un po per capire, anche se la richiesta è semplice il codice che hai postato mi ha disorientato. Ancora non sono sicuro, ma il codice che hai postato è parziale giusto?

Prova a inserire questa riga nel file myMenu.cpp:

extern void myLcdPrint( int8_t index);

PS: non mi piace proprio, non l'ho mai visto fare in C++ una cosa simile, cioè si trattasse di una sola funzione potresti documentarla per metterla in risalto.

Riguardo a extern leggi a seguire sul perché è necessario:

Ciao.

Ti ringrazio per la sollecota risposta e confermo che il codice è parziale.
Ho provato il tuo suggerimento ma purtroppo non cambia le cose ....
E' come se l'operatore risolutore di Ambito :: non funzionasse o come se quando il compilatore arriva a generare il file obj di myMenu.cpp non avesse traccia dello spazio globale del file.ino.
Ho provato allora a mettere nel file myMenu.h la seguente dichiarazione
extern void myLcdPrint( int8_t);
e provando a compilare sia con i :: che senza ottengo un nuovo tipo di errore :
error: storage class specified for 'myLcdPrint' extern void myLcdPrint( int8_t);
oppure
error: '::myLcdPrint' has not been declared ::myLcdPrint ( dum_c) ;
è un mistero ....

Mi interessa capire perchè non è possibile scrivere un codice come quello postato perchè ,tra l'altro, apparentemente contraddice quello che ho letto nei manuali di c++ e quello che risponde ChatGPT alla stessa domanda. Per il programma , se non trovo una soluzione, cerchero di trasformare tutti i

cpa2Lcd -> print ( MENU.. [] ); 
// in
cpa2Lcd -> print ( F( "......." ));
// dove il vettore carattere MENU..[] è sostituito nel sorgente con la stringa di origine "......"

Rimane sempre il dubbio sul perchè non funziona l'operatore ::
Ti ringrazio in ogni caso per la risposta.....
Ciao

Non saprei a me funziona, ha sempre funzionato, ci sarà qualcosa che sbagli, prova a creare uno sketch minimale e postalo così possiamo capire da dove deriva quell'errore.

Provato ora ora e funziona e so anche perché funziona, ma non so come mai non funziona a te. Funziona sia con :: che senza.

#include "moduleb.h"
// File moduleb.cpp
extern void externFunction();

MyClass::MyClass() {

}

MyClass::~MyClass() {

}

void MyClass::myMethod() {
    externFunction();
}

Il file header:

#ifndef moduleb_h
#define moduleb_h
// File moduleb.h
class MyClass {
    public:
    MyClass();
    ~MyClass();
    void myMethod();
};


#endif

Il file .ino:

#include "moduleb.h"

void externFunction() {
    Serial.println("Funziona!");
}

MyClass *ptrMyClass;

void setup()
{
    Serial.begin(115200);
    ptrMyClass = new MyClass();
    ptrMyClass->myMethod();

}

void loop () {

}

Ciao.

Grazie,
Domani provo e poi ti dico come è andata.
Ciao

Ti ringrazio,
Ho provato la tua soluzione ed è perfettamente funzionante. In pratica rispetto a quello che avevo scritto io ci sono solo due differenze:
La prima è che nel file cpp la definizione "extern void externfunction () " è prima del costruttore
La seconda è che vel file .h la definizione della funzione myMethod è pubblica e non privata.
Utilizzando questa semplice funzione ricavata dal forum :slight_smile:

	extern int __bss_end ;
  extern void *__brkval ;

  int memoryFree () 
  {
      int freeValue ;
      if ((int) __brkval == 0 ){ freeValue = ((int)&freeValue) - ((int)&__bss_end) ;}
      else {freeValue = ((int)&freeValue) - ((int)__brkval ) ;}
      return freeValue ;
  }

ho potuto verificare che dopo l'inizializzazione del programma ( creati tutti gli oggetti ) la RAM libera è passata da 192 bytes a 1434 bytes. Così dovrei riuscire a completare il progetto.
Mi sapresti spiegare perchè parrebbe così importante la posizione della definizione extern nel file cpp ?
Grazie ancora per avermi risolto il problema
Ciao

Nel file usi una funzione che il compilatore non è in grado di risolvere.

Aggiungendo le righe

extern int __bss_end ;
extern void *__brkval ;

stai definendo due prototipi di funzione e quindi queste due funzioni le puoi usare nel tuo metodo.
Allo stesso momento dici al compilatore che il codice di quelle funzioni si troverà su un file esterno al tuo .cpp
A quel punto il linker è in grado di mettere insieme tutti i pezzi e produrre il firmware compilato correttamente.

Se per fare un test aggiungi un prototipo di funzione extern, ma poi non lo definisci da nessuna parte, potrai osservare che il compilatore non ti da errori, ma il linker non è in grado di generare il file binario correttamente.

1 Like

Se deve essere private: non puoi certo chiamare il metodo da istanza di classe, ma puoi solo chiamarlo da metodi interni alla classe. In ogni caso extern risolve la visibilità del simbolo. Ma attenzione che l'uso smodato di extern non è pratica comune ed è sconsigliato in tutti i manuali. In sostanza extern fa male al programmatore che prima o poi non ci capisce più nulla.

Torna utile extern con lcd, ad esempio:

// nel file .ino
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);

La variabile istanza lcd è visibile solo nel modulo .ino (modulo che è una compile-unit) ma non lo è nelle altre compile-unit.

Diventa visibile in una compile-unit se aggiungi:

#include <LiquidCrystal.h>
extern LiquidCrystal lcd;

Potrai quindi usare il simbolo lcd per come sei abituato, sia dentro una funzione che dentro un metodo di classe.

Dipende tu dove l'hai scritta, in genere basta scriverla prima di usare il simbolo extern, certo che se lo metti dopo non ha molto senso, stessa cosa inserirlo a caso.

PS: Riguardo a chatGPT et company, perdi solo tempo, l'unico valido motivo per usarli e se ti pagano per allenarle a me non mi pagano e mi sono scocciato di correggerle sempre.

Ciao.

Hai ovviamente ragione,
Del fatto che non potesse essere private me ne ero già accorto ma l'errore che facevo era di inserire l'istruzione con "extern .... " dopo il metodo che la doveva usare. Posizinandola il testa al cpp o almeno prima del metodo che la usa funziona. Ti ringrazio per avermi sottolineato questa particolarità del compilatore a cui non avevo prestato sufficiente attenzione.
Per quanto riguara l'uso di extend concordo con te sull'uso smodato. In effetti è la prima volta che la uso perchè preferisco usare le classi con relative chiamate per puntatore. Nelle classi, poi, cerco di definire pubblici il minor numero di metodi e varabili possibile.
Per quanto riguarda ChatGPT , hai anche quì ragione da vendere. La sto provando per curiosità ma , a parte esempi molto semplici , da risposte molto spesso sbagliate.....

P.S. il tuo ( ? ) sito https://programmersqtcpp.blogspot.com/ è veramente molto interessante,
ho salvato l'indirizzo per future consultazioni......

Ciao

Grazie, sembra banale ma ha richiesto molto impegno onde evitare di creare un blog copia privo di contenuti come quelli che trovi in testa alla SERP.

Una precisazione è sempre dovuta riguardo al significato dei termini definire e dichiarare che negli ultimo decennio molti considerano sinonimi. Ho dichiarato guerra contro la disinformazione ma ancora non ho definito come combatterò, come quali armi e quanto è grande il mio esercito. Possiamo dire che una dichiarazione è una definizione povera, carente di dettagli, ma è una forzatura.

Ciao.

Verissimo ......
Ciao

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.