Implantation d'objets dans 1 liste [C] - Programmation
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 :
|
Ensuite, tu n'as plus qu'à écrire :
Code :
|
pour définir un type liste sur OBJ., et
Code :
|
pour l'utiliser.
Ainsi, En supposant que tu as une fonction comme suit:
Code :
|
si tu écris:
Code :
|
Les 2 premiers appels à add() seront OK, par contre, le compilateur C t'interdira les 2 appels suivants.
[edit]--Message édité par BifaceMcLeOD--[/edit]
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+,
Marsh Posté le 30-05-2001 à 09:42:52
wooh putain ! cette usine à gaz !
(moi je passerais en C++ ! )
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 ?
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
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...).
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
@+
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]
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();
}
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" );
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 ?
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...
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
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