OVH Cloud OVH Cloud

parcourir une structure

65 réponses
Avatar
Julien SCORDIA
Bonjour à tous,

J'ai une structure coucou comprenant des pointeurs. Dans certaines
conditions je cherche à allouer ou à libérer ces pointeurs. Je peux tout
faire à la main sans problème. Mais étant donné qu'un plus ou moins grand
nombre de ces pointeurs existe réellement dans la structure (du fait de
constantes préprocesseurs), j'aimerais ne pas avoir à changer mes fonctions
d'allocation et de libération de cette structure à chaque fois que je
rajoute un pointeur dans la structure ou que je change une constante
préprocesseur (par exemple).
C'est pourquoi j'aimerais pouvoir parcourir les divers pointeurs présents
dans la structure sans avoir à me soucier de leur nom, avec une sorte
d'itérateur. L'idée serait alors de tester le type du pointeur en cours, et
d'en déduire la bonne méthode d'allocation ou libération.
Comment peut-on réaliser cela en C?

Par ailleurs, je travaille sur un projet C depuis un bon moment, le
programme commence à être relativement conséquent (tout est relatif, c'est
un programme d'informatique scientifique: le nombre de lignes en ferait
sans doute sourire plus d'un sur ce forum). Un bon nombre de constantes
préprocesseurs sont bien pratiques (imbrication parfois de trois #ifdef
avec des && et || préprocesseurs), mais peuvent parfois gêner la lisibilité
et le parcours du code (sans le fameux % de vim, cela serait vraiment
laborieux). Que pensent les programmeurs professionnels et expérimentés des
constantes préprocesseur?

Merci d'avance pour vos conseils avisés,

Julien
--
"Allez, Monsieur, allez, et la foi vous viendra." (D'Alembert).

10 réponses

3 4 5 6 7
Avatar
Julien SCORDIA
Wallace wrote:

Cela est bien rigolo, mais un tantinet illisible (cependant, je ne
développe pas professionnellement en C, donc mon avis est peut-être
non pertinent...).


Oui, c'est illisible, ce qui a motivé ma question.

Qui plus est, je me pose une petite question : comment comptez-vous
utiliser les membres de votre structure ? En effet, quand bien même
vous conserveriez cette structure alambiquée et vous auriez trouvé
une manière d'itérer sur ses membres (votre question d'origine, si
je ne m'abuse), que feriez-vous exactement du résultat de cette
itération ?


Aujourd'hui suivant la constante préprocesseur, je fait telle ou telle chose
avec les membres de la structure, qui existent ou pas. L'itération sur les
membres ne
m'intéresse que pour l'allocation des pointeurs et la libérations de ces
derniers.

Bref, dans la mesure où la tronche de votre structure va dépendre de
toutes vos "constantes de compilation" (pardonnez la terminologie
incertaine), quel comportement universel voulez-vous faire avec votre
itération ?


Allocation et libération des pointeurs dans la structure (qui peuvent être
très particulières, et donc faire appel à des fonctions séparées qui
existent déjà). Non?
--
"Allez, Monsieur, allez, et la foi vous viendra." (D'Alembert).

Avatar
Julien SCORDIA
Stephane Legras-Decussy wrote:

tu te prends la tête sur des trucs "gadget" de C qui cachent une grosse
mauvaise
conception en fait...


Salut,

Merci pour ta réponse.
Si je viens poser cette question concernant les variables préprocesseur,
c'est que j'ai conscience du fait que ce n'est pas très lisible.
Cela dit, si le programme que je développe n'est pas professionnel, il n'en
reste pas moins relativement bien écrit (aucune variable globale, un bon
nombre de fonctions) et sain (valgrind ne bronche pas, on ne peut pas en
dire autant de tous les programmes sous Linux, il me semble).

Maintenant, afin de voir un peu de code et m'en inspirer, je repose ma
question: auriez-vous un projet sous Linux à me conseiller, dont je
pourrais lire le code? Un projet où le même type de problème se poserait, à
savoir une structure "à géométrie variable" avec juste ce qu'il faut
suivant le choix de l'utilisateur.

Julien
--
"Allez, Monsieur, allez, et la foi vous viendra." (D'Alembert).

Avatar
Julien SCORDIA
Harpo wrote:

Pour en revenir au sujet, un forum concernant un langage particulier
n'est pas compétent pour cela.
C'est dommage car il me semble qu'il y a des personnes ici qui sont
compétentes en ce domaine.


Merci pour ta réponse.
Je ne vois pas pourquoi le forum n'est pas adapté, car j'ai posté du C dans
certains de mes posts de ce thread, et j'attends bien des solutions en C.
Concernant la conception du projet, le fait d'avoir mis ces constantes
préprocesseur ne doit pas préjuger du reste du programme (que je revendique
de relative bonne qualité). Les constantes préprocesseurs sont bien une
possibilité offerte par le C, après tout, je ne suis pas dans un milieu
d'informaticiens, loin de là, donc en tant qu'autodidacte, je ne vois pas
pourquoi je m'en serais privé, d'autant plus que ni le K&R, ni le H&S,
indiquent de ne pas les utiliser (de mémoire).

Mon problème est d'avoir une structure avec juste ce qu'il faut suivant le
cas à traiter (pour l'exemple donné plus haut, à savoir si l'on est dans la
MANGROVE, sur MARS, ou au GROENLAND). Cette structure contient dans mon cas
précis tout un tas de pointeurs, que je veux allouer et libérer
automatiquement en itérant sur la structure, au lieu de reporter les mêmes
constantes préprocesseur dans la fonction d'allocation et libération: je
vois bien que ces constantes ne sont pas très lisibles, donc je viens vous
voir afin de ne les employer tout au plus que dans la définition de la
structure.

Des diverses réponses j'en ai retenu qu'en C je pouvais itérer sur la
structure, en définissant des structures de données supplémentaires, et en
jouant sur des "offset" de pointeur. Mais pour appeler la bonne fonction
d'allocation sur les pointeurs rencontrés dans la structure de manière
automatique, il fallait de la détection de type à l'exécution.
--
"Allez, Monsieur, allez, et la foi vous viendra." (D'Alembert).

Avatar
Julien SCORDIA
Jean-Marc Bourguet wrote:

Merci pour ta réponse.

Tu m'expliques que tu as besoin d'une structure avec des
champs variants suivant les define donnes lors de la
compilation. Je n'ai toujours pas compris quel est le
problème qui impose une telle solution. Je n'ai pas non
plus compris comment on utilise une telle structure sans
placer des ifdef partout, ce qui a priori est une mauvaise
idée.


Oui, les ifdef se propagent alors dans tout le programme, ce qui me pose
problème au niveau lisibilité et surtout maintenance du code, d'où ma
question sur ce forum (voir post original). En itérant sur la structure, et
en pouvant détecter le type du champ en cours, il serait possible d'appeler
la bonne méthode d'allocation, sans même avoir à connaître le nom du champ,
ou si tel ou tel champ existe vraiment.

Les principales alternatives ont déjà été données (plusieurs
structures, plusieurs définition de la même structure mais
n'en prendre qu'une, avoir dans la structure une table de
propriétés) mais t'indiquer quel est la bonne sans avoir une
idée du problème (jusqu'à présent on a une idée des
problèmes que te pose la solution que tu as choisie, ce qui
n'est pas du tout la même chose), c'est hors de ma
compétance.


Je recopie la réponse faite à HARPO plus bas:

Mon problème est d'avoir une structure avec juste ce qu'il faut suivant le
cas à traiter (pour l'exemple donné plus haut, à savoir si l'on est dans la
MANGROVE, sur MARS, ou au GROENLAND). Cette structure contient dans mon cas
précis tout un tas de pointeurs, que je veux allouer et libérer
automatiquement en itérant sur la structure, au lieu de reporter les mêmes
constantes préprocesseur dans la fonction d'allocation et libération: je
vois bien que ces constantes ne sont pas très lisibles, donc je viens vous
voir afin de ne les employer tout au plus que dans la définition de la
structure.

Des diverses réponses j'en ai retenu qu'en C je pouvais itérer sur la
structure, en définissant des structures de données supplémentaires, et en
jouant sur des "offset" de pointeur. Mais pour appeler la bonne fonction
d'allocation sur les pointeurs rencontrés dans la structure de manière
automatique, il fallait de la détection de type à l'exécution.
--
"Allez, Monsieur, allez, et la foi vous viendra." (D'Alembert).

Avatar
Thierry Boudet
On 2005-04-15, Harpo wrote:

. des choses relatives à la plateforme comme l'endianness, si le stack
va vers le haut ou le bas et des choses aussi palpitantes.
. des options de génération du logiciel (genre with-feature blah).
. Des choses qu'on est obligé de tester si on veut avoir des chances de
tourner sur plusieurs systèmes.

Je rajouterais plusieurs choses qui sont gérables par les #if:


o les messages/fenêtres/io de trace.
o le code utilisé pour les tests unitaires.
o le code de chronométrage.

J'ai compté récemment sur un kluge que je confectionne, ça
représente peut-être un quart des lignes. Dans ce quart, il
y a probablement beaucoup de trucs qui sont devenus inutiles,
mais dont je risque d'avoir besoin dans quelques mois/années.

C'est dommage car il me semble qu'il y a des personnes ici qui sont
compétentes en ce domaine.


Probablement pas moi :)

--
_/°< coin

Avatar
Jean-Marc Bourguet
Julien SCORDIA writes:

Mon problème est [...]


À nouveau, ce n'est pas ton problème c'est la solution que
tu as choisie pour ton problème. Sans connaître ton
problème -- et ton analogie ne m'y aide absolument pas -- on
ne peux qu'esquisser des solutions. Outre celles qui ont
déjà été citées, il y a aussi de la génération de code par
un programme séparé en fonction d'une configuration, une
utilisation plus masquante des macros dans le genre

#ifdef NEED_MANGROVE
#define DECL_MANGROVE int field1;
char* field2;
#define INIT_MANGROVE(s) (s).field1 = 5;
(s).field2 = NULL
#else
#define DECL_MANGROVE
#define INIT_MANGROVE(s)
#endif

struct s {
...
DECL_MANGROVE
};

struct s* allocate_struct_s()
{
s = malloc(sizeof struct s);
INIT_MANGROVE(*s);
}

mais sans connaître ton problème réel (autremement dit
exprime le sans utiliser de terme C) c'est difficile de dire
ce qu'il te faut choisir dans les 5 ou 6 solutions qui t'ont
déjà été données qui ne nécessitent pas d'introspection.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Harpo
Julien SCORDIA wrote:

Harpo wrote:

Je ne vois pas pourquoi le forum n'est pas adapté, car j'ai posté du C
dans certains de mes posts de ce thread, et j'attends bien des
solutions en C. Concernant la conception du projet, le fait d'avoir
mis ces constantes préprocesseur ne doit pas préjuger du reste du
programme (que je revendique de relative bonne qualité). Les
constantes préprocesseurs sont bien une possibilité offerte par le C,
après tout, je ne suis pas dans un milieu d'informaticiens, loin de
là, donc en tant qu'autodidacte, je ne vois pas pourquoi je m'en
serais privé, d'autant plus que ni le K&R, ni le H&S, indiquent de ne
pas les utiliser (de mémoire).


Il ne s'agit pas de ne pas les utiliser, mais une utilisation trop
intensive *peut* masquer un défaut de conception et rend le programme
peu lisible.


Mon problème est d'avoir une structure avec juste ce qu'il faut
suivant le cas à traiter (pour l'exemple donné plus haut, à savoir si
l'on est dans la MANGROVE, sur MARS, ou au GROENLAND). Cette structure
contient dans mon cas précis tout un tas de pointeurs, que je veux
allouer et libérer automatiquement en itérant sur la structure, au
lieu de reporter les mêmes constantes préprocesseur dans la fonction
d'allocation et libération: je vois bien que ces constantes ne sont
pas très lisibles, donc je viens vous voir afin de ne les employer
tout au plus que dans la définition de la structure.


Soit c'est su au moment de la compilation du programme, auquel cas on
peut utiliser le préprocesseur, mais on peut se demander s'il ne vaut
pas mieux faire 3 programmes et des fonctions communes au 3 programmes.

Soit ce n'est pas su au moment de la compilation et dans ce cas le
préprocesseur ne peut guère être de grande utilité.
Il est necessaire d'avoir quelque part une variable qui indique quel
est le type de maison, mangrove, mars pu Groënland.

J'ai fait un petit exemple, je ne l'ai pas testé, j'ai remplacé mangrove
par igloo, hlm, cabane car je ne connais pas de manière détaillée les
caractéristiques des maisons martiennes :

Les différentes unions et structures ont été détaillées, il y a moyen de
faire plus concis.
==================================== #include <stdio.h>
#include <stdlib.h>

// ----------------- .h

typedef enum
{
// La maison peut être un igloo, un hlm et une cabane
igloo, hlm, cabane
} MaisonType ;

typedef struct
{
int temperature ;
unsigned int diametre ;
// des pointeurs
} Igloo ;

typedef struct
{
unsigned int etages ;
unsigned int largeur ;
unsigned int longueur ;
// un tas de pointeurs
} Hlm ;

typedef struct
{
unsigned int hauteur ;
} Cabane ;

typedef union
{
Igloo igloo ;
Hlm hlm ;
Cabane cabane ;
} Maison ;


typedef struct
{
MaisonType type ;
Maison * maison ;

} MaisonPtr ;

// -----------------
int
main( void )
{
MaisonPtr m ;

// on alloue une maison, un igloo mais ça aurait pu être autre chose
m.maison = malloc( sizeof( *( m.maison ) ) ) ;
// tester le malloc

m.type = igloo ;
m.maison->igloo.temperature = -20 ;
m.maison->igloo.diametre = 12 ;
// allouer la mémoire et renseigner les pointeurs de l'igloo


// il faut désallouer la maison
if ( m.type == igloo )
{
printf("temp : %dn", m.maison->igloo.temperature) ;
// liberer la mémoire de l'igloo
}

free( m.maison ) ;

//

exit( EXIT_SUCCESS ) ;
}
=====================================

Avatar
Harpo
Thierry Boudet wrote:

On 2005-04-15, Harpo wrote:

. des choses relatives à la plateforme comme l'endianness, si le
stack va vers le haut ou le bas et des choses aussi palpitantes.
. des options de génération du logiciel (genre with-feature blah).
. Des choses qu'on est obligé de tester si on veut avoir des chances
de tourner sur plusieurs systèmes.

Je rajouterais plusieurs choses qui sont gérables par les #if:


o les messages/fenêtres/io de trace.
o le code utilisé pour les tests unitaires.
o le code de chronométrage.


Pour ce genre de choses, je mets dans un .h des choses du genre :

#if ( DEBUG > 0 )

#define maTrace(...)
fprintf( stderr, __VA_ARGS__ ),
fflush(NULL) ;

etc. ...

#else // ( DEBUG > 0 )

#define maTrace(...)

etc. ...

#endif // ( DEBUG > 0 )

et dans le .c

maTrace( "maVariable=%dn", maVariable ) ;

On peut faire ça pour pas mal de choses.
Cela clarifie grandement les sources en mettant le maximum de choses
dans des .h qui deviennent surtout du langage préprocesseur.



J'ai compté récemment sur un kluge que je confectionne, ça
représente peut-être un quart des lignes. Dans ce quart, il
y a probablement beaucoup de trucs qui sont devenus inutiles,
mais dont je risque d'avoir besoin dans quelques mois/années.


Le problème est celui de la pénibilité de lecture des programmes.


Avatar
Julien SCORDIA
Harpo wrote:

D'abord merci pour ta réponse et ta solution à base d'unions.

Soit c'est su au moment de la compilation du programme, auquel cas on
peut utiliser le préprocesseur, mais on peut se demander s'il ne vaut
pas mieux faire 3 programmes et des fonctions communes au 3 programmes.


C'est ce à quoi j'avais pensé par un moment.
Seulement, pour chacune des "maisons" correspondant à chacun des programmes,
les fonctions se seraient drôlement ressemblées. Franchement, il aurait été
très dur voire impossible de faire des fonctions communes de manière à ne
pas avoir de redondances.
Aujourd'hui, j'ai environ 40 "maisons" différentes.
En fait mes maisons sont des architectures de véhicules différentes, avec on
sans batterie, avec ou sans moteur électrique, hybride série, parallèle, ou
série-parallèle (par exemple, il y a bien d'autres options). Le programme
utilise la programmation dynamique (principe de Bellman) pour optimiser la
consommation de carburant sur un cycle vitesse/temps connu par avance
(c'est pourquoi j'ai tout intérêt à ce que le programme soit optimisé,
c'est gourmand en mémoire et en temps de calcul).
J'ai bien découpé mon programme en diverses fonctions.
Mais suivant l'architecture de véhicule considérée parmi les 40, des
différences apparaissent un peu partout, dont j'ai tenu compte avec mes #if
defined(...).
Dans un fichier parameters.h de mon projet, il y a le #define VEHICLE 23
(par exemple véhicule 23) qui permet de compiler le projet avec la bonne
architecture.



J'ai fait un petit exemple, je ne l'ai pas testé, j'ai remplacé mangrove
par igloo, hlm, cabane car je ne connais pas de manière détaillée les
caractéristiques des maisons martiennes :

Les différentes unions et structures ont été détaillées, il y a moyen de
faire plus concis.
==================================== > #include <stdio.h>
#include <stdlib.h>

// ----------------- .h

typedef enum
{
// La maison peut être un igloo, un hlm et une cabane
igloo, hlm, cabane
} MaisonType ;

typedef struct
{
int temperature ;
unsigned int diametre ;
// des pointeurs
} Igloo ;

typedef struct
{
unsigned int etages ;
unsigned int largeur ;
unsigned int longueur ;
// un tas de pointeurs
} Hlm ;

typedef struct
{
unsigned int hauteur ;
} Cabane ;

typedef union
{
Igloo igloo ;
Hlm hlm ;
Cabane cabane ;
} Maison ;


typedef struct
{
MaisonType type ;
Maison * maison ;

} MaisonPtr ;

// -----------------
int
main( void )
{
MaisonPtr m ;

// on alloue une maison, un igloo mais ça aurait pu être autre chose
m.maison = malloc( sizeof( *( m.maison ) ) ) ;
// tester le malloc

m.type = igloo ;
m.maison->igloo.temperature = -20 ;
m.maison->igloo.diametre = 12 ;
// allouer la mémoire et renseigner les pointeurs de l'igloo


// il faut désallouer la maison
if ( m.type == igloo )
{
printf("temp : %dn", m.maison->igloo.temperature) ;
// liberer la mémoire de l'igloo
}

free( m.maison ) ;

//

exit( EXIT_SUCCESS ) ;
}
====================================
--

"Allez, Monsieur, allez, et la foi vous viendra." (D'Alembert).

Avatar
Julien SCORDIA
Harpo wrote:

D'abord merci pour ta réponse et ta solution à base d'unions.

Soit c'est su au moment de la compilation du programme, auquel cas on
peut utiliser le préprocesseur, mais on peut se demander s'il ne vaut
pas mieux faire 3 programmes et des fonctions communes au 3 programmes.


C'est ce à quoi j'avais pensé par un moment.
Seulement, pour chacune des "maisons" correspondant à chacun des programmes,
les fonctions se seraient drôlement ressemblées. Franchement, il aurait été
très dur voire impossible de faire des fonctions communes de manière à ne
pas avoir de redondances.
Aujourd'hui, j'ai environ 40 "maisons" différentes. Pour répondre à la
demande de Jean-Marc Bourguet (post récent plus haut dans ce thread), voici
mon problème.
En fait mes maisons sont des architectures de véhicules différentes, avec on
sans batterie, avec ou sans moteur électrique, hybride série, parallèle, ou
série-parallèle (par exemple, il y a bien d'autres options). Le programme
utilise la programmation dynamique (principe de Bellman) pour optimiser la
consommation de carburant sur un cycle vitesse/temps connu par avance
(c'est pourquoi j'ai tout intérêt à ce que le programme soit optimisé,
c'est gourmand en mémoire et en temps de calcul).
J'ai bien découpé mon programme en diverses fonctions.
Mais suivant l'architecture de véhicule considérée parmi les 40, des
différences apparaissent un peu partout, dont j'ai tenu compte avec mes #if
defined(...).
Dans un fichier parameters.h de mon projet, il y a le #define VEHICLE 23
(par exemple véhicule 23) qui permet de compiler le projet avec la bonne
architecture.

Avec ta solution à base d'unions ou les autres proposées, je trouve que les
choses ne sont pas forcément plus simples (peut-être un peu plus lisible
mais moins intuitif). Mais je me trompe peut-être.

L'idée de mon post initial était donc de me débarrasser des #ifdef, là où
peut-être ce serait possible; il me semblait qu'avec un itérateur potable
j'aurais pu les éviter au moins dans les fonctions d'allocation et de
libération de véhicule. Ceci en itérant sur la structure véhicule et ses
structures sous-jacentes (la structure véhicule est déjà l'union des
diverses structures comprises dans le véhicule: moteur thermique,
électrique, transmission (boîte de vitesse, courroie, etc.), batterie,
génératrice pour certains véhicules donc pour certaines constantes
préprocesseur, etc.).

Je vous remercie tous pour vos réponses, je vais essayer de rassembler
toutes ces idées et de trouver le meilleur compromis.

Julien
--
"Allez, Monsieur, allez, et la foi vous viendra." (D'Alembert).

3 4 5 6 7