[C] Implantation d'objets dans 1 liste

Implantation d'objets dans 1 liste [C] - Programmation

Marsh Posté le 29-05-2001 à 23:18:32    

J'aimerais pouvoir implanter n'importe quels types d'objets dans une même liste dont la définition est celle-ci :
 
typedef struct cel{
OBJ obj;
struct cel * lien_prec;  
struct cel * lien_svt;
} cell;
 
typedef struct {
cell * tete;
cell * queue;
} LISTE;  
 
Actuellement je ne peux q'utiliser le type OBJ.
Pour corser le tout l'objet devra être en dur dans la cellule, cell, donc pas de pointeur sur objets du genre void * obj.
 
Si une âme charitable serait m'aiguiller  
 
Merci

Reply

Marsh Posté le 29-05-2001 à 23:18:32   

Reply

Marsh Posté le 30-05-2001 à 00:59:03    

Ta question n'est pas très claire : est-ce que tu veux implémenter un type liste à la fois générique et typé ?
C'est-à-dire qu'à priori, on peut utiliser ton type liste pour n'importe quel type d'objet, mais une fois le type d'objet choisi, seul ce type-là n'est utilisable ?
 
Si seule la généricité totale (tout type d'objet) t'intéresse, pas le choix, il te faut définir OBJ comme étant void*. Enfin je veux dire, c'est de très loin ce qu'il y a de plus simple.
 
Par contre, si tu veux pouvoir profiter à la fois de la généricité au niveau source et du typage fort, il va falloir jouer serré avec le préprocesseur.
 
Je te propose ce qui suit (de mémoire, parce que j'ai fait ce genre de choses il y a longtemps) :

Code :
  1. #define _paste3(a,b,c)   a##b##c
  2. #define _paste4(a,b,c,d) a##b##c##d
  3. #define  paste3(a,b,c)   _paste3(a,b,c)
  4. #define  paste4(a,b,c,d) _paste4(a,b,c,d)
  5. #define LIST_TYPE(infotype)  paste3(List,_,infotype)
  6. #define _CELL_TYPE(infotype) paste4(_,Cell,_,infotype)
  7. #define CELL_TYPE(infotype)  paste3(Cell,_,infotype)
  8. #define DECLARE_LIST_TYPE(infotype) \
  9.       typedef struct _CELL_TYPE(infotype) { \
  10.          infotype obj; \
  11.          struct _CELL_TYPE(infotype)* lien_prec; \
  12.          struct _CELL_TYPE(infotype)* lien_svt; \
  13.       } STRUCT_TYPE(infotype); \
  14.          \
  15.       typedef struct { \
  16.          CELL_TYPE(infotype)* tete; \
  17.          CELL_TYPE(infotype)* queue; \
  18.       } LIST_TYPE(infotype)


 
Ensuite, tu n'as plus qu'à écrire :

Code :
  1. DECLARE_LIST_TYPE(OBJ);


pour définir un type liste sur OBJ., et

Code :
  1. LIST_TYPE(OBJ)


pour l'utiliser.
 
Ainsi, En supposant que tu as une fonction comme suit:

Code :
  1. void add(LIST_TYPE(infotype) list, infotype info);


 
si tu écris:

Code :
  1. DECLARE_LIST_TYPE(int);
  2.     DECLARE_LIST_TYPE(char*);
  3.     typedef LIST_TYPE(int) List_int;
  4.     typedef LIST_TYPE(char*) List_String;
  5.     List_int     list_n;
  6.     List_String  list_s;
  7.     add(list_n, 2);
  8.     add(list_s, "toto" );
  9.     add(list_s, 2);
  10.     add(list_n, "toto" );


 
Les 2 premiers appels à add() seront OK, par contre, le compilateur C t'interdira les 2 appels suivants.

 

[edit]--Message édité par BifaceMcLeOD--[/edit]

Reply

Marsh Posté le 30-05-2001 à 02:44:12    

Il a tout dit le Face de Bi (:D):
Soit tu as une liste non typee avec des void*
Soit tu imites ce que faisaient les convertisseurs C++->C du debut avec plein de macros indigestes.
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 30-05-2001 à 09:42:52    

wooh putain ! cette usine à gaz ! :ouch:
 
:lol:  
 
(moi je passerais en C++ ! :lol: )

Reply

Marsh Posté le 30-05-2001 à 10:49:13    

c'est vrai qu'en c++ et avec la STL c'est juste un peu plus simple ...
y'a une bonne raison de faire ca en C ?

Reply

Marsh Posté le 30-05-2001 à 23:56:05    

BifaceMcLeOD,
 
C'est vrai ma question n'est pas trés claire, c'est en fait l'objet obj que je ne souhaiterais pas typé. J'aimerais pouvoir simuler ceci :
 
typedef struct cel{  
void obj;  
struct cel * lien_prec;  
struct cel * lien_svt;  
} cell;  
 
typedef struct {  
cell * tete;  
cell * queue;  
} LISTE;
 
La raison pour laquelle je ne souhaite pas utiliser de void * pour obj est d'éviter que l'utilisateur de mes primitives ne vire un obj sans avoir fait le nécessaire dans la cellule, cell , contenant le pointeur sur cet obj.
 
Peux tu m'expliquer en quelques mots tes 4 premières déclarations #define _paste3(a,b,c) a##b##c ....  
 
Merci à toi

Reply

Marsh Posté le 31-05-2001 à 00:09:26    

Dans ce cas, tu n'as pas le choix, il faut que tu définisses OBJ comme un void*, ou que ton champ "obj" soit en fait un pointeur sur ton type OBJ (ce qui revient au même du point de vue conceptuel, mais rajoute un niveau de pointeur dans la pratique).
 
Pour ta dernière question, " ## " est un opérateur du préprocesseur qui colle les 2 chaînes de caractères qui sont autour. Ainsi, si tu écris "List##_##OBJ", le compilateur recevra "List_OBJ". Et si tu écris (en utilisant mes macros) "CELL_TYPE(MonType)", le préprocesseur transformera cela en "paste3(Cell,_,MonType)", donc en "Cell##_##MonType", par conséquent en "Cell_MonType". Et le compilateur recevra cette dernière chaîne de caractères uniquement (qui comme par hasard ;) est un identificateur de type valide...).

Reply

Marsh Posté le 31-05-2001 à 00:25:38    

Bon dommage, j'vais m'orienter vers du void * obj qui offre tout de même l'avantage d'utiliser cet obj dans d'autres structures (Arbres, piles, ...).
 
Merci de ton aide  
 
@+

Reply

Marsh Posté le 31-05-2001 à 01:17:44    

ET les templates en C++ c'est pas mieux ?

Reply

Marsh Posté le 31-05-2001 à 03:27:19    

Pschitt a écrit a écrit :

J'aimerais pouvoir implanter n'importe quels types d'objets dans une même liste dont la définition est celle-ci :
 
typedef struct cel{
OBJ obj;
struct cel * lien_prec;  
struct cel * lien_svt;
} cell;
 
typedef struct {
cell * tete;
cell * queue;
} LISTE;  
 
Actuellement je ne peux q'utiliser le type OBJ.
Pour corser le tout l'objet devra être en dur dans la cellule, cell, donc pas de pointeur sur objets du genre void * obj.
 
Si une âme charitable serait m'aiguiller  
 
Merci




 
Redefinit cell ainsi:
 
typedef struct cell{
struct cell * lien_prec;  
struct cell * lien_svt;
int celltype;
} cell;
 
Cell ne contient pas l'objet, mais on peut simuler une clsse dérivée C++ en C, en incluant la structure cell comme premier membre de la nouvelle structure:
 
typedef struct cell_OBJ1{
  cell c;
  OBJ1 o;
} cell_OBJ1;
 
typedef struct cell_OBJ2{
  cell c;
  OBJ2 o;
} cell_OBJ2;
 
Puis définit des fonctions constructeurs de chaque type de cellule, qui appeleront les constructeurs de la structure parente "cell" avant d'initialiser les champs de l'objet inclus:
 
function cell *new_cell(celltype, size){
  cell *pc = calloc(size, 1);
  pc->c.celltype = celltype;
}
function cell_OBJ1 * new_cellOBJ1(){
  cell_OBJ1 *pc = (cell_OBJ1 *)new_cell(1, sizeof(cell_OBJ1));
  pc->o.xxx = ...
}
function cell_OBJ2 * new_cellOBJ2(){
  cell_OBJ1 *pc = (cell_OBJ2 *)new_cell(2, sizeof(cell_OBJ2));
  pc->o.yyy = ...
}
 
Quand tu parcoures la liste ces cellules, tu dois utiliser un pointeur de type (cell *pc), et tu peux utiliser le membre
pc->celltype pour savoir quel type de cellule tu as, avant de le transtyper dans le bon type de cellule et d'accéder aux champs spécifiques.
 
Si tu as des champs communs ou si le nombre de types d'objets est restreint, tu peux aussi utiliser une union contenant les différents types d'objets, et tout mettre dans la structure cell au lieu de créer une structure de cellule par type d'objet.

 

[edit]--Message édité par verdy_p--[/edit]

Reply

Marsh Posté le 31-05-2001 à 03:27:19   

Reply

Marsh Posté le 01-06-2001 à 00:17:46    

verdy_p,  
 
Je suis en train de bosser sur ta méthode, ça se présente pas mal sauf que j'ai encore du mal au niveau des castings pour lier les cellules entre elles, VC++ m'indique pas mal d'erreurs & warnings. Je n'arrive également pas à faire pointer, tete, sur la 1ère cellule de la liste.
 
Voici mon code :
 
Objets :
 
typedef struct cell{
struct cell * lien_prec;  
struct cell * lien_svt;
int celltype;
} cell;
 
typedef struct {
char nom[25];
} OBJ1;
 
typedef struct {
char nom[25];
} OBJ2;
 
typedef struct cell_OBJ1{
  cell c;
  OBJ1 obj;
} cell_OBJ1;
 
typedef struct cell_OBJ2{
  cell c;
  OBJ2 obj;
} cell_OBJ2;
 
typedef struct LISTE{
void * tete;
void * queue;
} LISTE;
 
Fonctions :
 
LISTE * CreerListe()
{
LISTE * l;
if((l = NEW(LISTE)) == NULL) return (l);
else
  l->tete = NULL;
  l->queue = NULL;
return (l);
}
 
cell * new_cell(int celltype, int size)
{cell * pc = calloc(1,size);
 if(pc==NULL)
 
  return (cell*)NULL;
 
 pc->celltype=celltype;
 return pc;
}
 
cell_OBJ1 * new_cellOBJ1()
{ return (cell_OBJ1*)new_cell(1,sizeof(cell_OBJ1));}
 
cell_OBJ2 * new_cellOBJ2()
{ return (cell_OBJ2*)new_cell(1,sizeof(cell_OBJ2));}    
 
Main :
 
void main()
{
 LISTE * l=NULL;
 cell_OBJ1 * tmp1=NULL;
 cell_OBJ2 * tmp2=NULL;
 
 l=CreerListe();
 tmp1=new_cellOBJ1();  
 tmp2=new_cellOBJ2();
 
 memcpy(tmp1->obj.nom,"Lulu",25*sizeof(char));
 memcpy(tmp2->obj.nom,"Toto",25*sizeof(char));
 
 printf("%s\n",tmp1->obj.nom);
 printf("%s\n",tmp2->obj.nom);
 
 tmp1->c.lien_prec=NULL;
 tmp1->c.celltype=1;
 (cell_OBJ2)tmp1->c.lien_svt=tmp2; /* VC++ n'arrive pas à caster */
 tmp2->c.lien_svt=NULL;
 
 (cell_OBJ1 *)l->tete=tmp1; /* ça passe mais après je n'accède pas à l->tete->obj.nom */
 
 getchar();
}

Reply

Marsh Posté le 01-06-2001 à 08:15:35    

Pschitt a écrit a écrit :

verdy_p,  
 
// snip
 
 (cell_OBJ2)tmp1->c.lien_svt=tmp2; /* VC++ n'arrive pas à caster */
 tmp2->c.lien_svt=NULL;
 
 (cell_OBJ1 *)l->tete=tmp1; /* ça passe mais après je n'accède pas à l->tete->obj.nom */
}




 
Hello, essaie de bien parenthéser ton cast... peut-être que ça ira mieux (?)
 
Sinon, une question toute bête (je suis nouveau et j'arrive en pleine cogitation ;-) ), quel intérêt d'avoir les objets dans les cellules et pas avoir un bête void * à l'extérieur ?
 
Pour l'exercice de style, voilà une solution toute bête mais pas très belle (quoique...)
 
le type cellule (simplement chaîné, sinon ça fait mal à la tête ;-)
 
typedef struct _cellule
{
  struct _cellule *svt;
  int type;
} Cellule, *ListeCh;
 
pour l'ajout, on passe le numéro du type, un pointeur sur l'objet, et la taille en octet du type... on ajoute simplement l'objet à la suite de la cellule.
 
#define CHAMPVAL(x) (&x->type+1)
 
int ajout_tete(ListeCh *l, int type, void *el, int size)
{
  Cellule *c;
 
  if ( !(c=(Cellule*)malloc(sizeof(Cellule)+size)) )
    return 0;
 
  c->type = type;
  c->svt = *l;
  memcpy(CHAMPVAL(c), el, size);
 
  *l = c;
 
  return 1;
}
 
bref, après ça, inutile de vous faire un dessin, un fois que l'on a un Cellule *, on utilise CHAMPVAL pour avoir un pointeur (de type int *, il faut donc caster) sur le champ valeur, et roule ma poule.
 
l'insertion n'est pas très jolie, mais on s'y fait, par exemple :
 
  int a;
  ListeCh maliste=NULL;
 
  a=1515;
  ajout_tete(&maliste, TYPE_INT, &a, sizeof(int));
  ajout_tete(&maliste, TYPE_STRING, "tralala", 8);
 
après, tu peux t'écrire tes macros qui vont bien pour chacun de tes types préférés...
 
#define AJOUT_INT(l,x)   ajout_tete(&l, TYPE_INT, &x, sizeof(int))
#define AJOUT_STRING(l,x) ajout_tete(&,l, TYPE_STRING, x, strlen(x)+1)
 
a=10;
AJOUT_INT(maliste, a);
AJOUT_STRING(maliste, "tralala" );

Reply

Marsh Posté le 01-06-2001 à 08:47:14    

list<string> maliste1;
maliste1.push_front("abc" );
maliste1.push_back("def" );
 
list<int> maliste2;
maliste2.push_front(2);
maliste2.push_back(3);
 
.... c pas mal la STL non ;) ?

Reply

Marsh Posté le 02-06-2001 à 00:10:04    

Ben oui, mais quand on est obligé de se cantoner au C, c'est un dur d'utilisation, la STL...  :sarcastic:  ;)

Reply

Marsh Posté le 02-06-2001 à 01:36:58    

OK, c'est trés clair, le C++ permet de faire ça + simplement mais le C++ je ne connais pas encore. Pour moi, l'heure est à l'apprentissage de l'algorithmique et de la programmation. Voila 8 mois(Cours du soir au CNAM et surtout travail perso) que j'ai attaqué et je commence à me faire plaisir. On a beaucoup bosser sur le C et je trouve ça trés bien, pour démarrer, se familiariser à algorithmique, pointeurs, structures, ... Et puis c'est un language qui à quand même fait parler de lui et qui est encore largement utilisé il me semble (portabilité, rapidité, ...). Cela dit le C++ m'intéresse et je vais pas tarder à voir de + près.
 
Au fait c'est OK pour mes soucis de pointeurs :
 
(cell_OBJ2)tmp1->c.lien_svt=tmp2; /* VC++ n'arrive pas à caster */  
tmp2->c.lien_svt=NULL;  
   
(cell_OBJ1 *)l->tete=tmp1; /* ça passe mais je n'accède pas à l->tete->obj.nom */  
 
Comme ça c'est mieux :
 
((cell_OBJ2 *)tmp1->c.lien_svt)=tmp2;
((cell_OBJ1 *)l->tete)=tmp1;
 
Reste + qu'a mettre tous ça dans des fonctions les + générique possibles avec de void *, des #define partout et tout baigne
 
Je bataille, je bataille mais c'est comme ça que je gagne
 
Merci à vous tous pour votre aide  
 
Tchao

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed