Tableau dynamique (en C++)

Bonjour,

Je me demandais comment faire pour stocker un tableau et ceci de manière dynamique ? Je ne me souviens plus comment le faire en C++… laissez moi vous préciser ce que je souhaite faire.

J’ai un objet qui possède un “collection” variables d’entiers… ainsi il me faudrait un tableau dynamique. L’objectif étant d’écrire un code qui permet de gérer un nombres quelconques d’item. Pour être plus précis, j’ai une classe qui gère un Menu. J’ai défini un certains nombres d’item possible pour mes menus. Et je veux que les items du menu soit personnalisables et donc le but est de passer avant la gestion du menu, un tableau des items du menu en question.

Concrètement j’aimerai faire cela :

Menu monMenuA;
uint8_t itemsMenuA[3] = {ITEM_B, ITEM_C, ITEM_D};
monMenuA.initialize(itemsMenuA);

Menu monMenuB;
uint8_t itemsMenuB[2] = {ITEM_A, ITEM_E};
monMenuB.initialize(itemsMenuB);

Je comptais utiliser memcpy pour la copie du tableau, en écrivant :

memcpy(dest_array, src_array, sizeof(src_array));

Mais en déclarant mon tableau, dans Menu de la manière suivante : uint8_t * items; j’ai une erreur à l’exécution…

Puis-je le faire avec de simple tableau ? Ou dois-je faire une implémentation de collection (par exemple une liste chainée) pour cela ?

Merci d’avance

1 Like

Je vais peut-être dire une bêtise mais selon ce qu’il y a dans tes ITEM_A ITEM_B etc tu pourrais utiliser une chaîne de caractères en utilisant les caractères qui correspondent aux entier que tu veux représenter ? Du coup tu n’aurais plus le problème de la longueur du tableau dynamique.

Merci pour ton conseil Codnpix, je vais voir qu’elle solution je retiens.

J’ai essayé de régler mon problème avec un malloc je n’ai plus de crash du programme… mais j’ai un autre nouveau soucis. J’ai essayé avec une déduction du nombre d’items avec sizeof… et j’ai également essayé de passer le nombre d’items en paramètre… mais dans les deux cas j’ai un ou deux items fantômes qui s’ajoute à mon menu. :cry:

Je manque d’idée, si vous en avez de nouvelle je prends :slight_smile:

J’avais essayé d’avancer sur ce point mais si je ne dis pas de bêtise, on m’avait déconseillé cette méthode sur notre type de matériel car cela fragmente la mémoire et il est difficile de mettre en place les mécanisme de nettoyage (J’espère que je me rappelle bien mais certains pourront me corriger si je dis des bêtises). Mieux vaut je crois bloquer un tableau de la taille maximale et mettre un indicateur de fin ou utiliser un compteur qui va donner le nombre d’éléments réellement utilisés. L’inconvénient c’est que tu reserveras sans doute plus de place que tu n’en as besoin mais si je comprends bien ce qui m’avait été expliqué, ton programme fonctionnera plus longtemps. A faire vérifier par les bons dans ce genre d’opérations (ce qui n’est pas mon cas, donc prend ce message comme une indication…)

1 Like

Merci Jicehel !

Tu as peut être ce post Allocation dynamique : bonne ou mauvaise idée #POO en tête ?

Mais là je ne parle de faire des malloc ou autre opérations de ce type à de multiples reprises. Non, mon programme compte seulement deux malloc (pour toutes son exécution) ce qui je pense est acceptable :slight_smile:

2 Likes

Exactement.
En embarqué sur un microcontrôleurs, l’allocation dynamique est une mauvaise pratique car ça se termine toujours mal.
Il est préférable de réserver la mémoire nécessaire dès le départ, idéalement dans un tableau statique, ou éventuellement par un malloc() dans le setup() pour toute la durée d’exécution du programme.

2 Likes

Dans cet exemple, les items sont connus dès le départ. Il ne semble pas nécessaire de faire de l’allocation : la fonction initialize(uint8_t *) pourrait simplement conserver le pointeur sur les items, sans les copier.
NOTE : il faudrait tout de même lui fournir le nombre d’items :

Menu monMenuA;
uint8_t itemsMenuA[3] = {ITEM_B, ITEM_C, ITEM_D };
initialize(itemsMenuA, sizeof(itemsMenuA)/sizeof(itemsMenuA[0])) // sizeof(A)/sizeof(A[0]) = count of

ou, variante, utiliser un item avec une valeur spécifique comme marqueur de fin.

#DEFINE ITEM_END 0 // le "0" n'est pas utilisable comme valeur de item, et marque la fin des données, comme pour les chaines C/C++.
Menu monMenuA;
uint8_t itemsMenuA[3] = {ITEM_B, ITEM_C, ITEM_D, ITEM_END };
monMenuA.initialize(itemsMenuA);

NOTE : attention toutefois à la déclaration du tableau itemsMenuA[] : la déclaration doit rester valide tout le temps. Il faut que cette déclaration soit globale (en dehors des fonctions).

1 Like

Merci pour ton aide JMP, en revanche je bloque sur un problème relatif a la première solution. J’ai déclaré mes tableaux dans le .ino… avant l’instanciation statique de mon objet mère mais le compilateur retourne une erreur : variable hors de portée… l’un de mes menus est un attribut de la classe principale et j’en ai un autre dans une autre classe (qui elle est un attribut de la classe principale). La solution que je vois est de mettre des setters sur la classe principale pour initialiser. Mais tu as peut être une autre solution ? J’ai fais un truc comme ça :

static uint8_t menuAlphaItems[] = {ITEM_B, ITEM_C, ITEM_D}; // même erreur avec ou sans le mot-clé static
// déclaration semblable du menuBetaItems

Application app;

// reste du code...
1 Like

Il faudrait voir le code, j’ai du mal à me faire une idée…

Quelques lignes de code plus tard… tu trouveras sur ce dépôt , en particulier sur la branche test-with-bug : un petit POC.

Je pense qu’une des solutions est de faire une méthode qui passe en paramètre l’item vers le menu, comme par exemple :

// dans setup
app.initializeAppMenu(appMenuItems, sizeof(appMenuItems) / sizeof(appMenuItems[0]));

// implémentation de initializeAppMenu
void Application::initializeAppMenu(uint8_t * items, size_t aNbItems) {
  this->appMenu.initialize(items, aNbItems);
}

// Note : aNbItems peut être calculé dans initializeAppMenu :)

Si tu as une autre solution, je prends également :slight_smile:

Merci d’avance

ça ne marche pas car dans Application.cpp, la variable appMenuItems n’est pas définie. Il faut ajouter extern uint8_t appMenuItems[3]; (extern signifiant “elle a été définie ailleurs, laisse le linker se démerder”)

Sinon, pourquoi ne pas simplement en faire une variable de la classe Application (et const tant qu’à faire). Si tu en as besoin en dehors de la classe Application alors tu peux la mettre public.

1 Like

Comme le dit @minirop , c’est un problème de portée.
Ce qui me semblerait plus logique, c’est de passer tout simplement les items de menu à la app.initialize(), qui les repasserait ensuite à appMenu.initialize().

void setup() {
  gb.begin();
  gb.display.setPalette(PALETTE);
//  app.initialize(  );
  app.initialize( appMenuItems, sizeof(appMenuItems) / sizeof(appMenuItems[0]) ); // 
}

Merci à vous deux ! (désolé de répondre si tardivement…)

Je ne connaissais pas le mot-clé extern, merci @minirop ! Pour te répondre sur le choix de placer mon tableau en attribut : je préfère le dissocier de la classe que je souhaite paramétrable. Dans l’exemple de code il n’y a qu’un menu, mais dans l’application finale il y en a deux (et il sont sensiblement différents).