Objet d'une classe en paramètre dans une autre classe

En effet, ça ne fonctionne pas... Mais, chronologiquement non plus :confused:

menuLCD.h

#ifndef menuLCD_h
#define menuLCD_h

#include "ExaNumericLcdMidasI2C.h"


class MenuLCD
{
  public :
    MenuLCD(LcdMidasI2c &lcd);  //Constructor
    getTest(void);
  private :
    LcdMidasI2c &_lcd;  //LCD MIDAS I2C Librarie ExaNumericLcdMidasI2C
};

#endif

menuLCD.cpp

#include "menuLCD.h"

MenuLCD::MenuLCD(LcdMidasI2c &lcd)
{
  _lcd = lcd;
}

MenuLCD::getTest(void)
{
  _lcd.clear(); 
  _lcd.setCursor(0,0); //Colonne, Ligne
  _lcd.print("Hello world");
}

main.ino

#include <LiquidCrystal.h>
#include "menuLCD.h"

LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
MenuLCD menuUser(lcd); //Ici, je cherche à faire passer en paramètre l'objet lcd de façon à pouvoir utiliser 

void setup () {
  Serial.begin(9600);
  Serial.flush();

  lcd.begin(16, 2);
}

void loop() {
 menuUser.getTest();
}

Sauf que rien ne s'affiche

Et hop on passe d'un LCD à un LCD I2C ...

LcdMidasI2c : connais pas. Quelle librairie ?

Tu as essayé d'afficher avec un sketch simple ?

Il faut que la class de l'instance que tu passes dans le constructeur soit la même (ou une class héritée) que celle déclarée dans le constructeur.
Dans le constructeur tu as une class LcdMidasI2c

   MenuLCD(LcdMidasI2c &lcd);  //Constructor

et tu lui passes un objet de class LiquidCrystal

LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
MenuLCD menuUser(lcd); //Ici, je cherche à faire passer en paramètre l'objet lcd de façon à pouvoir utiliser

Étonnant que cela puisse compiler non ?

Je reprends :

hbachetti:
Et hop on passe d'un LCD à un LCD I2C ...

LcdMidasI2c : connais pas. Quelle librairie ?

Tu as essayé d'afficher avec un sketch simple ?

C'est la même librarie que liquidSchristal, j'ai juste refais mes appels pour correspondre a celui que j'utilise. Mêmes fonctions, même nom, mêmes variables toussa.

hbachetti:
Étonnant que cela puisse compiler non ?

En effet ! Je suis même surpris que ça fonctionne en partie...

Ce qui me fais pensé à:
1- est-ce que je pointe au bon endroit ?
2- est-ce que le contenu de mon pointeur à changé ?
3- comment vérifier que je passe bien l'instance créé dans le main, dans ma classe ?
4- existe-t-il une solution plus adhéquate ?

Bref, je suis dans le doute :roll_eyes:

Sans le code de ExaNumericLcdMidasI2C.h impossible de dire quoi que ce soit.

Voilà donc le .h de la classe LCD.

Et bien...
Ça fonctionne !

En fait, c'est un problème de pointeur. J'ai fini par me poser la question, est-ce que j'affecte le pointeur avec le bon endroit ? Et la réponse était pire, le pointeur n'étais tout simplement pas rempli. Rien à voir avec la classe exa... Bref !

Je met la solution au cas ou ça en intéresserais d'autres :
Donc, mon main, réduit pour les tests :

#include <LiquidCrystal.h>
#include "menuLCD.h"        //Le chemin de ma classe qui héritera de l'objet lcd

LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
MenuLCD menuUser(lcd); //Ici, nous instancions la classe MenuLCD avec l'objet lcd passé en paramètre pointeur

void setup () {
  Serial.begin(9600);
  Serial.flush();

  lcd.begin(16, 2); //Utilisation de la classe LiquidChristal ou de exa.. Nombre de colonnes 16, nombre de ligne 2

  menuUser.getTest(); //ici, nous affichons le contenu de getTest() qui fera appel à l'objet passé en paramètre
}

void loop() {
 //Vide pour les essais !
}

Voyons, donc le .h de ma classe:

#ifndef menuLCD_h
#define menuLCD_h

#include <LiquidCrystal.h>  //La librairie parente pour laquelle nous cherchons à utiliser ses membres

class MenuLCD
{
  public :
    MenuLCD(LiquidCrystal &lcd);  //Constructeur avec passage en paramètre de l'objet au travers d'un pointeur
    void getTest(void);
    
  private :
    LiquidCrystal *_lcd;  //L'instanciation de la classe liquidCrystal avec comme paramètres, ceux de l'objet pointé
};

La différence était ici... dans la partie private ! mais pas que...

Voyons donc le .cpp

#include "menuLCD.h"

MenuLCD::MenuLCD(LiquidCrystal &lcd) //Passage en paramètre de l'objet
{
  _lcd = &lcd; //Nous remplissons le pointeur avec l'objet pointé
}

void MenuLCD::getTest(void)
{
  _lcd->clear(); //Autre subtilité, nous ne passons plus par un "." mais par "->"... Car on fait référence à l'objet pointé
  _lcd->setCursor(0,0); //Colonne, Ligne
  _lcd->print("Hello world");
}

Bien entendu, ça fonctionne parfaitement !

Je peux donc clore ce tread...

Tu utilises inutilement par des pointeurs.

C'est quasiment identique à la solution présentée par hbachetti au post #3 sauf que sa solution est beaucoup plus élégante.

Je n'avais pas stocké le lcd dans la classe sous forme de pointeur, mais de référence.
Cela permettait entre autres de ne pas transformer tout tes appels _lcd.clear(), _lcd.setCursor(), etc. en _lcd->clear(), lcd->setCursor, etc.
Mais bon, cela revient au même.

Alors, que ce passe-t-il avec l'autre solution ?
Par ce que, du coup... ça jette un doute ! En effet, si c'est pas élégant, c'est qu'il y a moyen d'optimiser le code et là, ou ouvre la suite du sujet.

Ma question sera donc, comment modifier le code qui fonctionne par celui qui devrais fonctionné et qui est élégant.

C'est intéressant de creuser ce sujet.

De l'extérieur ce n'est pas grave :wink: ta solution fonctionne bien, c'est juste qu'en général on fait un choix de notation : référence ou pointeur. Dans ton code tu as les 2 notations (passage par référence de l'objet mais sa mémorisation est faite avec un pointeur). Cela n'impacte pas sur l'efficacité du code, c'est juste que le code est un poil plus compliqué à lire et qu'on se pose la question pourquoi ce passage de notation "référence" à notation "pointeur". Si tu veux que cela soit parfait fait comme hbachetti, n'utilise que des références (plus d'infos sur les références : Cours de C/C++).

Tout à fait.
Juste une question d'écriture. C'est plus facile de taper _lcd.clear() que _lcd->clear().

    LiquidCrystal &_lcd;  // référence
    LiquidCrystal *_lcd;  // pointeur

    _lcd.clear();    // référence
    _lcd->clear(); // pointeur

Ok pour ces informations.

Mais, alors pourquoi ça compile et ça plante le µC quand j'utilise les références ?

Joue au jeu des 7 erreurs entre ton code avec référence et celui de hbachetti. Il y en a forcément au moins une :wink:

Un peu de dérision :

supercc:
Joue au jeu des 7 erreurs entre ton code avec référence et celui de hbachetti. Il y en a forcément au moins une :wink:

Avec ce genre de réponse, à part parler pour ne rien dire...

Un peu d'essais avec l'exemple donné, cité, car le blabla ne m'intéresse pas:
Le .h

#ifndef menuLCD_h
#define menuLCD_h

#include <LiquidCrystal.h>

class MenuLCD
{
  public :
    MenuLCD(LiquidCrystal &lcd);  //Constructor
    void getTest(void);
  private :
    LiquidCrystal &_lcd;
};

#endif

le .cpp
#include "menuLCD.h"

MenuLCD::MenuLCD(LiquidCrystal &lcd)
{
_lcd = lcd;
}

void MenuLCD::getTest(void)
{
_lcd.clear();
_lcd.setCursor(0,0); //Colonne, Ligne
_lcd.print("Hello world");
}

Le résultat :
Cela ne produit rien à part un joli crash du µC... Comme j'ai dit plus haut. Pourtant, il n'y a pas de différence au niveau pointeur. Ou alors, j'ai mal lu !

Ok, je pensais que c'était une bête erreur de copie...

Au final, je n'ai pas bien compris, quand tu inclues LiquidCrystal.h, c'est le .h officiel ou celui que tu as modifié ? Parce que ta version diffère pas mal (au niveau des constructeurs) de la version officielle.

Geeks:
Cela ne produit rien à part un joli crash du µC... Comme j'ai dit plus haut. Pourtant, il n'y a pas de différence au niveau pointeur. Ou alors, j'ai mal lu !

Effectivement, tu as raison, il ne faut pas affecter la valeur passée à la référence mais appeler le constructeur pour l'initialiser.

menu.cpp

#include "menuLCD.h"

MenuLCD::MenuLCD(LiquidCrystal &lcd)
: _lcd(lcd)
{
}

void MenuLCD::getTest(void)
{
 _lcd.clear(); 
 _lcd.setCursor(0,0); //Colonne, Ligne
 _lcd.print("Hello world");
}

Super top cet example de code !!!

Maintenant, une question de newbies ....
Comment faire pour que la création de l'objet lcd qui se fait avant le setup se trouve directement dans la classe MenuLCD ?

En fait, je cherche a faire ca pour ne pas avoir a créer cette première instance dans le code source mais que tout se passe directement dans la classe que l'on a instanciée ...

Question de newbies, mais bon, je ne sais pas (encore) faire :stuck_out_tongue:

Manu