Initialiser une classe avec un pointeur sur tableau

Bonjour

Je galère avec les pointeurs, les tableaux. Je viens du monde java, donc pas étonnant...
Voilà un petit programme avec une classe "Palette" pour travailler sur des couleurs. La palette peut avoir une dimension quelconque (trois fois une puissance de 2), codage gif oblige. Pour des palettes triviales, le modèle est inclus dans la classe sous forme d'un tableau d'octets. Pour des palettes plus complexes, on les crée à l'extérieur de la classe et on les lui transmet. Voir les deux approches dans le "setup" et les constructeurs correspondants.
Le stockage de l'adresse de la palette en cours dans la classe est fait dans une variable "myPalette". Une méthode triviale "printPalette" imprime le contenu sur le moniteur série en une suite d'octets en hexa.

#include "arduino.h"
class Palette { // a palette of rgb colors
  private :
    char * myPalette [] ;
    int size ;
    const char bw [6] = { // 2 colors
        0x00, 0x00, 0x00, /* 0 -> black */
        0xFF, 0xFF, 0xFF, /* 1 -> white */
    } ;
    const char mini4 [12] = { // 4 colors
        0x00, 0x00, 0x00, /* 0 -> black */
        0xFF, 0x00, 0x00, /* 1 -> red */
        0x00, 0xFF, 0x00, /* 2 -> green */
        0x00, 0x00, 0xFF  /* 3 -> blue */
    } ;
  public :
    Palette (int standard) {
        if (standard == 2) myPalette = bw  ;
        else myPalette = mini4  ;
        size = sizeof (maPalette) ;
    }
    Palette (char external [], int size) {
        myPalette = external ; 
        this->size = size ;
    }
    void printPalette () {
        for (int i = 0 ; i < size ; i++) {
            if (maPalette[i] < 10) Serial.print (" 0x0") ; else Serial.print (" 0x") ;
            Serial.print (maPalette[i], HEX) ;
        }
        Serial.println () ;
    }
} ;

void setup {
    Serial.begin (19200) ;
    (new Palette (4))->printPalette () ;
    (new Palette ({0x40, 0x40, 0x40, 0xC0, 0xC0, 0xC0}, 6))->printPalette () ;
}
void loop {}

Voilà, je coince depuis 24h, incapable de déclarer correctement mes tableaux, ma variable pointeur, les constructeurs et une méthode triviale pour travailler sur la classe. Un conseil pour remettre ça d'équerre ? Un virtuose du C++ peut il corriger ça pour que ça se compile et que ça fonctionne, et si possible expliquer pourquoi ce brouillon "brut de java" ne fonctionne pas en c++ ?

plein de chose à dire - quelques points pour vous aider à comprendre pourquoi ça ne marche pas comme cela:

  • en premier lieu ne pas utiliser char comme type si vous voulez en fait les valeurs entre 0 et 255 (un char est signé, donc -128 à 127).

  • donc vous pourriez déclarer

byte bw [6] = { // 2 colors
        0x00, 0x00, 0x00, /* 0 -> black */
        0xFF, 0xFF, 0xFF, /* 1 -> white */
    } ;

Dans ce cas le type associé au nom du tableau est byte * donc votre variable myPalette devrait être déclarée comme

byte * myPalette ;

c'est un pointeur

  • quand on utilise le nom du tableau en tant que pointeur, il se passe ce qu'on appelle un 'decay'. Le compilateur perd les informations de type associées au tableau. Le type connu par le compilateur de bw par exemple c'est [nobbc]byte [6][/nobbc] il sait que c'est un tableau de 6 bytes. Mais au moment où vous allez faire l'affectation au pointeur
if (standard == 2) myPalette = bw  ;
else myPalette = mini4  ;

vous mettez en jeu ce fameux 'decay' et le type connu n'est plus que byte*. Vous n'avez plus la dimension et donc quand vous essayez de faire

 size = sizeof (maPalette) ;

vous allez mettre dans size le nombre d'octets nécessaires pour représenter un pointeur (2 ou 4 suivant votre architecture)

==> ça devrait vous donner des idées

PS/ loop et setup sont des fonctions, il faut mettre des () juste après

void setup() {...}
void loop() {....}

EDIT: allez, pour vous mettre sur le bon chemin, un truc comme cela devrait compiler (tapé ici, non testé)

#include <Arduino.h>

enum tPalette : byte {BW_PALETTE, MINI4_PALETTE, CUSTOM}; // on définit un type énuméré pour faire beau 

class Palette { // a palette of rgb colors
  private :
    byte * palette;
    size_t paletteSize;

    byte bw [6] = { // 2 colors
      0x00, 0x00, 0x00, /* 0 -> black */
      0xFF, 0xFF, 0xFF, /* 1 -> white */
    };

    byte mini4 [12] = { // 4 colors
      0x00, 0x00, 0x00, /* 0 -> black */
      0xFF, 0x00, 0x00, /* 1 -> red */
      0x00, 0xFF, 0x00, /* 2 -> green */
      0x00, 0x00, 0xFF  /* 3 -> blue */
    };

  public :
    Palette (tPalette choix) {
      switch (choix) {
        case MINI4_PALETTE:
          palette = mini4; paletteSize = sizeof(mini4); break;
        case BW_PALETTE:
        default:
          palette = bw; paletteSize = sizeof(bw); break;
      }
    }

    Palette (byte * externalPalettePtr, size_t pSize) : palette (externalPalettePtr), paletteSize(pSize) {}

    void printPalette () {
      for (size_t i = 0; i < paletteSize / 3; i++) {
        Serial.print ("0x");
        for (byte rgb = 0; rgb < 3; rgb++) {
          size_t colorIndex = 3 * i + rgb;
          if (palette[colorIndex] < 0x10) Serial.write('0');
          Serial.print(palette[colorIndex], HEX);
        }
        if (i != paletteSize / 3 - 1)  Serial.write(',');
      }
      Serial.println ();
    }
};

byte customPalette[] = {0x40, 0x40, 0x40, 0xC0, 0xC0, 0xC0};
size_t customPaletteSize = sizeof(customPalette); // nombre total d'octets

void setup() {
  Serial.begin (115200);
  (new Palette(BW_PALETTE))->printPalette ();
  (new Palette(MINI4_PALETTE))->printPalette ();
  (new Palette(customPalette, customPaletteSize))->printPalette ();
}

void loop() {}

Notez que j'ai déclaré la customPalette[] comme variable globale. Si vous déclarez le tableau dans le setup() c'est une variable locale qui disparaîtra dès que le setup() sera fini et vous vous retrouverez avec un pointeur vers n'importe où dans la classe Palette. En cas d'usage d'une palette externe, il faut absolument que la mémoire associée soit durable.

Note aussi que vous dupliquez pour chaque palette de la mémoire utilisée pour bw et mini4... elles seraient mieux en variables globales statiques (connues que dans ce fichier) de votre bibliothèque. Donc un truc plutôt comme cela:

#ifndef PALETTE_H
#define PALETTE_H
#include <Arduino.h>

enum tPalette : byte {BW_PALETTE, MINI4_PALETTE, CUSTOM};

static byte bw[] = { // 2 colors
  0x00, 0x00, 0x00, /* 0 -> black */
  0xFF, 0xFF, 0xFF, /* 1 -> white */
};

static byte mini4[] = { // 4 colors
  0x00, 0x00, 0x00, /* 0 -> black */
  0xFF, 0x00, 0x00, /* 1 -> red */
  0x00, 0xFF, 0x00, /* 2 -> green */
  0x00, 0x00, 0xFF  /* 3 -> blue */
};

class Palette { // a palette of rgb colors
  private :
    byte * palette;
    size_t paletteSize;

  public :
    Palette (tPalette choix) {
      switch (choix) {
        case MINI4_PALETTE:
          palette = mini4; paletteSize = sizeof(mini4); break;
        case BW_PALETTE:
        default:
          palette = bw; paletteSize = sizeof(bw); break;
      }
    }

    Palette (byte * externalPalettePtr, size_t pSize) : palette (externalPalettePtr), paletteSize(pSize) {}

    void printPalette () {
      for (size_t i = 0; i < paletteSize / 3; i++) {
        Serial.print ("0x");
        for (byte rgb = 0; rgb < 3; rgb++) {
          size_t colorIndex = 3 * i + rgb;
          if (palette[colorIndex] < 0x10) Serial.write('0');
          Serial.print(palette[colorIndex], HEX);
        }
        if (i != paletteSize / 3 - 1)  Serial.write(',');
      }
      Serial.println ();
    }
};
#endif

byte customPalette[] = {0x40, 0x40, 0x40, 0xC0, 0xC0, 0xC0};
const size_t customPaletteSize = sizeof(customPalette); // nombre total d'octets

void setup() {
  Serial.begin (115200);
  (new Palette(BW_PALETTE))->printPalette ();
  (new Palette(MINI4_PALETTE))->printPalette ();
  (new Palette(customPalette, customPaletteSize))->printPalette ();
}

void loop() {}

si je ne me suis pas planté, le moniteur série (à 115200 bauds) affichera

[color=purple]
0x000000,0xFFFFFF
0x000000,0xFF0000,0x00FF00,0x0000FF
0x404040,0xC0C0C0
[/color]

Super, merci. Je n'ai testé que le dernier, qui me parait parfaitement répondre au besoin. Merci pour la leçon.

OK pour les Enum, j'aurais du y penser, c'est pareil en java. Pour le second constructeur, je me suis un peu gratté la tête... ce qui est après le ":" cela correspond à l'équivalent d'instructions d'initialisation pour les variables privées, je suppose. Est ce qu'on peut faire appel à un autre constructeur, comme lorsqu'on écrit "super()" en java pour appeler le constructeur d'une classe dont la classe en cours hérite ?

En C++, lorsque tu crées un objet d'un classe dérivée, le ctor de la classe parente est invoqué automatiquement, et bien sûr avant celui de la classe dérivée.
Du coup, quand tu définis le ctor d'une classe dérivée, il faut préciser commenr appeler le ctor de la parente:

class Derivee : public Parente {
public:
  Derivee ( des arguments );
}


Derivee::Derivee ( arguments... ) 
: Parente ( arguments nécessaires pour la classe parente ),
: autres inits
{
  code
}

Pour le second constructeur, je me suis un peu gratté la tête... ce qui est après le ":" cela correspond à l'équivalent d'instructions d'initialisation pour les variables privées, je suppose

oui c'est une notation qui permet de définir la 'member initializer list'. C'est mieux (notamment en cas de sous-classe) de faire comme cela plutôt que de mettre les affectations dans le code du constructeur.

OK, merci pour votre aide.

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