Partage
  • Partager sur Facebook
  • Partager sur Twitter

La POO et le C

Le clash !

    30 avril 2011 à 17:19:42

    Citation : Pouet_forever


    Je ne connais pas typeof, mais je connais le préprocesseur. Comme le préprocesseur passe avant toute étape de compilation etc. il en résultera que tu auras typeof(machin) et non le type de machin. Je suppose qu'il agit au même titre que sizeof.



    Merci pour la confirmation :)
    • Partager sur Facebook
    • Partager sur Twitter
      30 avril 2011 à 17:24:26

      Citation : Taurre


      EDIT: ne pas essayer avec un type composé de deux mots comme long long


      En effet, mais on peut toujours ruser. ;)

      typedef char* str;
      typedef long long Long;
      


      • Partager sur Facebook
      • Partager sur Twitter
      Zeste de Savoir, le site qui en a dans le citron !
        30 avril 2011 à 17:31:52

        Citation : GurneyH


        En effet, mais on peut toujours ruser.



        Cela fait plaisir de te revoir GurneyH ^^
        Sinon en effet, il y a moyen de ruser avec les typedef. D'ailleurs on peut aussi utiliser ceux de l'en-tête stdint.h.
        • Partager sur Facebook
        • Partager sur Twitter
          1 mai 2011 à 8:33:18

          Citation : Taurre

          Citation : heizmann


          mmhhh... :-° n'est-ce pas un peu dangereux ? Pas de contrôle de type (quoique... typeof marche avec gcc :( mais pas portable), et puis >_< il y a les effets de bords aussi...



          Non, parce que le but est en fait de définir plusieurs fonctions prenant des types différents, mais d'uniformiser l'appel via une macro. Voici un exemple:

          max.def




          #ifndef MAX_DEF
          #define MAX_DEF  1
          
          #define MAX_PROTOTYPE(TYPE) TYPE max_ ## TYPE (TYPE a, TYPE b)
          
          #define MAX_DEFINITION(TYPE) MAX_PROTOTYPE(TYPE) \
          { \
          	if ((a) > (b)) { \
          		return (a); \
          	} else { \
          		return (b); \
          	} \
          }
          
          #define MAX(a, b, TYPE) max_ ## TYPE ((a), (b))
          
          #endif /* MAX_DEF */
          



          max.h




          #ifndef MAX_H
          #define MAX_H  1
          
          #include "max.def"
          
          MAX_PROTOTYPE (int);
          MAX_PROTOTYPE (double);
          
          #endif /* MAX_H */
          



          max.c




          #include "max.h"
          
          MAX_DEFINITION (int)
          MAX_DEFINITION (double)
          



          main.c




          #include <stdio.h>
          #include <stdlib.h>
          
          #include "max.h"
          
          
          int
          main (void)
          {
          	printf ("%d\n", MAX (10, 20, int));
          	printf ("%f\n", MAX (10.50, 10.40, double));
          
          	return EXIT_SUCCESS;
          }
          



          Alors oui, je sais, cette fonction max est complètement inutile puisqu'elle pourrait-être implémentée via une simple macro, mais c'est juste pour l'exemple.

          Le but est donc de masquer les appels à des fonctions différentes via la macro MAX et de simplifier les définitions des différentes fonctions via les macros MAX_PROTOTYPE et MAX_DEFINITION. Dans la réalité la validité des types est bel et bien assurée puisqu'on appel une fonction différente suivant le type des arguments ;)

          EDIT: ne pas essayer avec un type composé de deux mots comme long long :-°



          ...cette astuce que j'ai déjà lue maintes et maintes fois, est désormais claire (côté comment ça marche, comment la réadapter à mes besoins) pour moi :) très bonne démarche pour la séparation des fichiers, et aussi pour le choix simpliste de l'exemple. Très pédagogique ! Je crois que je vais me servir de cette astuce pour simplifier quelques codes que j'ai pu pondre le long de ce fil :D

          Sinon, je me demande... cette astuce et les X-macros mélangées... y'a sûrement moyen d'en tirer quelque chose d'utile, nan ?

          Je vais tâcher d'activer mes neurones aujourd'hui pour voir si je trouve un truc sympa à réinvestir :)

          Décidément, ce fil est vraiment un bon échange, je trouve ! Ça part parfois en « sucette » ce qui permet de chopper de nouvelle idées ! Très dynamique et intéressant, et puis :-° ça trolle pas - rare pour un tel sujet :lol: !
          • Partager sur Facebook
          • Partager sur Twitter
            1 mai 2011 à 11:14:14

            Citation : heizmann


            ça trolle pas - rare pour un tel sujet



            +1 :D

            Sinon, il y a une question qui me trotte dans la tête depuis un petit temps concernant l'héritage. J'entends souvent dire qu'il y a deux techniques:

            - la première est d'inclure dans la structure fille une structure mère;
            - la seconde est de définir la structure fille exactement comme la mère et d'ajouter ses propres champs à la suite.

            Alors, du moment qu'il n'y a qu'un niveau d'héritage, aucune ne me semble poser problème, mais la première me semble franchement pas pratique si l'on a une suite d'héritage comme Personnage -> Humain -> Mage -> Enchanteur. Imaginons qu'on ait le champs "vie" dans la structure Personnage, on doit donc faire, self->mere->mere->mere->vie ? o_O

            De même, si l'on a l'idée farfelue d'utiliser le polymorphisme, cela ne me semble pas pouvoir fonctionner puisque, dans le cas où l'on passe une structure parente comme ceci par exemple: mage_recupere_mana (self->mere), la fonction utilisera la vtable de la structure Mage alors que l'on souhaite qu'elle utilise celle de la structure Enchanteur...

            Voilà je ne sais pas si j'ai été clair, mais si quelqu'un à un contre exemple je serais curieux de le voir :)
            • Partager sur Facebook
            • Partager sur Twitter
              1 mai 2011 à 11:31:27

              Oh que si, c'est très clair :D !!!

              hem... on voit souvent, effectivement, des trucs du genre : "recopier la struct mère dans la fille en respectant l'ordre des champs, puis ajouter le reste (l'héritage)"... perso, je n'aime pas ce genre de pratique, c'est dangereux, on peut inverser des champs, tout ça :-° ... enfin, sauf si on utilise correctement les X-macros comme je l'ai montré dans un de mes posts.

              En ce qui concerne l'idée d'un pointeur de la fille vers la mère, hum :-° ça ressemble du coup à une relation, et plus à de l'héritage... en effet, du coup, la mère "n'est plus dans la fille" (NDT : :D rien à voir avec les accouchements). Faudrait peut-être des mécanismes pour automatiser la recherche dans les parents ;) genre : mécanisme récursif, avec contrôle du type de l'objet crée, et aussi recherche des relations que la classe possède (parenté, interfaces...). Ce mécanisme pourrait être appelé au niveau des classes, mmhhh... :) c'est 'achement intéressant, hihi, je vais réfléchir pour proposer une solution en dessin pour tout à l'heure (big-schéma ;) on fait plus dans le tuto maintenant, on fait dans le scan-pédago-gribouillo-schéma :) uhuh)...

              EDIT ! Je suis en train de penser ( :-° eeeh oui, ça m'arrive) !... Il faudrait se réaccorder sur les "scopes" et sur le vocabulaire à employer : "une classe, c'est : ...", "un type de classe, c'est : ...". En clair, je propose qu'on songe tout doucement à la rédaction d'un cahier des charges musclé pour définir proprement notre implémentation objet ^^
              • Partager sur Facebook
              • Partager sur Twitter
                1 mai 2011 à 14:11:28

                Citation : heizmann


                Faudrait peut-être des mécanismes pour automatiser la recherche dans les parents ;) genre : mécanisme récursif, avec contrôle du type de l'objet crée, et aussi recherche des relations que la classe possède (parenté, interfaces...). Ce mécanisme pourrait être appelé au niveau des classes



                Cela m'a l'air franchement compliqué, mais je veux bien voir ça ;)

                Citation : heizmann


                hem... on voit souvent, effectivement, des trucs du genre : "recopier la struct mère dans la fille en respectant l'ordre des champs, puis ajouter le reste (l'héritage)"... perso, je n'aime pas ce genre de pratique, c'est dangereux, on peut inverser des champs, tout ça



                Avec des macros, il y a moyen de s'en sortir sans risque. Si je reprends ton idée de polymorphisme avec une vtable sous forme de structure, cela donne par exemple:


                personnage.h




                #ifndef PERSONNAGE_H
                #define PERSONNAGE_H  1
                
                #define PERSONNAGE_DEFINITION \
                	int vie; 
                
                #define PERSONNAGE_VTABLE \
                	void (*touche) (void*, int); \
                	void (*delete) (void*);
                
                
                typedef struct personnage personnage_s;
                
                
                personnage_s*
                personnage_create (void);
                
                void
                personnage_touche (void *_self, int degats);
                
                int
                personnage_vie (void *_self);
                
                void
                personnage_delete (void *_self);
                
                #endif /* PERSONNAGE_H */
                




                personnage.c




                #include <stdio.h>
                #include <stdlib.h>
                
                #include "personnage.h"
                
                
                struct personnage_vtable {
                	PERSONNAGE_VTABLE
                };
                
                
                struct personnage {
                	struct personnage_vtable *vtable;
                	PERSONNAGE_DEFINITION
                };
                
                
                
                static void
                touche (void *_self, int degats)
                {
                	personnage_s *self = _self;
                	
                	self->vie -= degats;
                }
                
                
                static void
                delete (void *_self)
                {
                	personnage_s *self = _self;
                
                	free (self);
                	puts ("destruction d'un personnage");
                }
                
                
                personnage_s*
                personnage_create (void)
                {
                	personnage_s *self;
                
                	if (!(self = malloc (sizeof *self))) {
                		return NULL;
                	}
                
                	if (!(self->vtable = malloc (sizeof *self->vtable))) {
                		free (self);
                		return NULL;
                	}
                
                	self->vie = 100;
                	self->vtable->touche = touche;
                	self->vtable->delete = delete;
                
                	return self;
                }
                
                
                void
                personnage_touche (void *_self, int degats)
                {
                	personnage_s *self = _self;
                
                	self->vtable->touche (self, degats);
                }
                
                
                int
                personnage_vie (void *_self)
                {
                	personnage_s *self = _self;
                
                	return self->vie;
                }
                
                
                void
                personnage_delete (void *_self)
                {
                	personnage_s *self = _self;
                
                	self->vtable->delete (self);
                }
                




                mage.h




                #ifndef MAGE_H
                #define MAGE_H  1
                
                
                #define MAGE_DEFINITION \
                	int mana;
                
                typedef struct mage mage_s;
                
                
                mage_s*
                mage_create (void);
                
                
                #endif /* MAGE_H */
                




                mage.c




                #include <stdio.h>
                #include <stdlib.h>
                
                #include "personnage.h"
                #include "mage.h"
                
                
                struct mage_vtable {
                	PERSONNAGE_VTABLE
                };
                
                
                struct mage {
                	struct mage_vtable *vtable;
                	PERSONNAGE_DEFINITION
                	MAGE_DEFINITION
                };
                
                
                
                static void
                touche (void *_self, int degats)
                {
                	mage_s *self = _self;
                
                	self->vie -= (degats - (self->mana / 10));
                }
                
                
                static void
                delete (void *_self)
                {
                	mage_s *self = _self;
                
                	free (self);
                	puts ("destruction d'un mage");
                }
                
                
                mage_s*
                mage_create (void)
                {
                	mage_s *self;
                	personnage_s *tmp;
                
                	if (!(tmp = personnage_create ())) {
                		return NULL;
                	}
                
                	if (!(self = realloc (tmp, sizeof *self))) {
                		personnage_delete (tmp);
                		return NULL;
                	}
                
                	self->vtable->touche = touche;
                	self->vtable->delete = delete;
                	self->mana = 100;
                
                	return self;
                }
                




                main.c




                #include <stdio.h>
                #include <stdlib.h>
                
                #include "personnage.h"
                #include "mage.h"
                
                
                
                int
                main (void)
                {
                	personnage_s *maurice;
                	mage_s *adalbert;
                
                	maurice = personnage_create ();
                	adalbert = mage_create ();
                
                	personnage_touche (maurice, 20);
                	personnage_touche (adalbert, 20);
                
                	printf ("maurice: %d\n", personnage_vie (maurice));
                	printf ("adalbert: %d\n", personnage_vie (adalbert));
                
                	personnage_delete (maurice);
                	personnage_delete (adalbert);
                
                	return EXIT_SUCCESS;
                }
                



                On a donc deux structures: la structure personnage et la structure mage. La structure mage hérite de la structure personnage. Les deux fonctions personnage_touche et personnage_delete peuvent être qualifiée de "virtuelle" en ce sens que la fonction appelée dépend du type de la structure passée en argument (comme les structures sont de tailles différentes cela était nécessaire pour les "destructeurs").

                Bon, l'aspect ennuyeux est qu'il faut passer par realloc après l'appel au constructeur de la structure mère puisque les deux structures n'ont pas la même taille. Dans le cas où la structure fille serait la mère d'une autre structure et que ces deux structures disposeraient de "fonctions virtuelles", il serait nécessaire d'agrandir également la vtable.

                Histoire d'éviter les cast lors de l'appel aux "fonctions virtuelles", j'ai utilisé des pointeurs générique et j'effectue les cast en interne.

                Pour résumer l'exemple, si un personnage se fait toucher il perd des dégats équivalent au nombre passé en argument à la fonction personnage_touche alors qu'un mage ne perd que les dégats diminué du dixième de son mana. Lors de l'appel à un "destructeur", ce dernier affiche s'il est celui de la structure personnage ou celui de la structure mage.

                Voilà, tout ça pour dire que je ne suis pas certains qu'il soit possible de faire de même avec un héritage basé sur l'inclusion des structures parentes dans les structures filles :)
                • Partager sur Facebook
                • Partager sur Twitter
                  1 mai 2011 à 14:24:58

                  :p c'est archi simple en effet ! Et bourré de pragmatisme ! J'aime bien :) !!!

                  Bon, il reste encore du travail... en effet, le principal serait de voir ce qu'on pourrait bien faire d'une réaffectation temporaire de VTables en dynamique... :) mmhhh...
                  • Partager sur Facebook
                  • Partager sur Twitter
                    1 mai 2011 à 19:37:39

                    Citation

                    Je ne connais pas typeof, mais je connais le préprocesseur. Comme le préprocesseur passe avant toute étape de compilation etc. il en résultera que tu auras
                    typeof(machin) et non le type de machin. Je suppose qu'il agit au même titre que sizeof.


                    En y repensant, en fait, c'est assez logique. typeof(x) ne peut être interprété par le préprocesseur.

                    Citation

                    Alors, du moment qu'il n'y a qu'un niveau d'héritage, aucune ne me semble poser problème, mais la première me semble franchement pas pratique si l'on a
                    une suite d'héritage comme Personnage -> Humain -> Mage -> Enchanteur. Imaginons qu'on ait le champs "vie" dans la structure Personnage, on doit donc faire,
                    self->mere->mere->mere->vie ?
                    o_O

                    De même, si l'on a l'idée farfelue d'utiliser le polymorphisme, cela ne me semble pas pouvoir fonctionner puisque, dans le cas où l'on passe une structure
                    parente comme ceci par exemple: mage_recupere_mana
                    (self->mere), la fonction utilisera la vtable de la structure Mage alors que l'on souhaite qu'elle utilise celle de la structure Enchanteur...

                    Voilà je ne sais pas si j'ai été clair, mais si quelqu'un à un contre exemple je serais curieux de le voir


                    En fait, ce que tu dis ne peut pas marcher avec des pointeurs. Si on choisit d'utiliser un premier membre de type structure en tant que premier champ pour représenter l'héritage, alors ça ne doit pas être un pointeur. Sinon, ça fait un genre d'héritage virtuel...


                    Sinon, à propos de vos codes précédents, certaines chose me font un peu sursauter :

                    @GurneyH: typedef long long Long;
                    Cette instruction ne risque pas de poser problème, étant donné que le type long existe déjà ?

                    @taurre: if (!(self->vtable = malloc (sizeof *self->vtable)))
                    Normalement, il n'y a pas besoin d'une vtable différente par objet créé. Tous les objets d'un même type partagent la même vtable, codée statiquement dès la compilation ou alors dans une phase d'initialisation du programme.
                    L'avantage de faire autrement, c'est qu'on peut customiser la vtable d'un objet en particulier. Pourquoi pas, mais en pratique c'est pas vraiment utile à mon avis.

                    Sinon, on peut se passer de realloc, si on partage la phase de construction en deux: allocation en mémoire puis appel du constructeur ensuite s'il y en a un. L'avantage de séparer les deux choses c'est que le constructeur fils peut appeler le constructeur père sans problème et sans avoir à tricher avec realloc. En plus, ça nous ajoute en quelque sorte la notion de constructeur par défaut...
                    • Partager sur Facebook
                    • Partager sur Twitter
                      1 mai 2011 à 19:42:33

                      Citation : Quentin C 2


                      @GurneyH: typedef long long Long;
                      Cette instruction ne risque pas de poser problème, étant donné que le type long existe déjà ?


                      Hum, c'est Long avec une majuscule.
                      après long long est un mot clef, donc...

                      Sauf grosse erreur
                      :-°


                      • Partager sur Facebook
                      • Partager sur Twitter
                      Zeste de Savoir, le site qui en a dans le citron !
                        1 mai 2011 à 20:15:01

                        Citation : QuentinC 2


                        Normalement, il n'y a pas besoin d'une vtable différente par objet créé. Tous les objets d'un même type partagent la même vtable, codée statiquement dès la compilation ou alors dans une phase d'initialisation du programme.



                        Ah! Je n'y avais pas pensé! Merci beaucoup! :)
                        C'est vrai que pour chaque structure ont peut se contenter d'une vtable statique, cela nous bouge le problème de son éventuelle réallocation (et de son allocation tout court).

                        Citation : QuentinC 2


                        Sinon, on peut se passer de realloc, si on partage la phase de construction en deux: allocation en mémoire puis appel du constructeur ensuite s'il y en a un. L'avantage de séparer les deux choses c'est que le constructeur fils peut appeler le constructeur père sans problème et sans avoir à tricher avec realloc. En plus, ça nous ajoute en quelque sorte la notion de constructeur par défaut...



                        Ça par contre j'y avais pensé :D
                        Mais, en effet cela évite des réallocation en cascade dans le cas d'une suite d'héritage.

                        EDIT: je me rends également compte que les cast dans les destructeurs sont inutiles :-°
                        • Partager sur Facebook
                        • Partager sur Twitter
                          1 mai 2011 à 23:30:22

                          Citation

                          Hum, c'est Long avec une majuscule.


                          Voilà ce qui arrive quand on lit trop vite... désolé, effectivement.

                          Citation

                          EDIT: je me rends également compte que les cast dans les destructeurs sont inutiles


                          ON peut aussi ajouter que si on n'a pas besoin d'afficher le message, alors on n'a pas besoin non plus de différencier les destructeurs puisque free sait ce qu'il y a à libérer.

                          Au fait ça m'amène à une question débile: pourquoi le C++ différencie-t-il delete et delete[] ? Quel est l'intérêt ? (Je ne parle pas de son utilisation mais bien de l'intérêt que ça a techniquement parlant... un bloc de mémoire est un bloc de mémoire)
                          • Partager sur Facebook
                          • Partager sur Twitter
                            1 mai 2011 à 23:34:20

                            Citation : QuentinC 2


                            @taurre: if (!(self->vtable = malloc (sizeof *self->vtable)))
                            Normalement, il n'y a pas besoin d'une vtable différente par objet créé. Tous les objets d'un même type partagent la même vtable, codée statiquement dès la compilation ou alors dans une phase d'initialisation du programme.
                            L'avantage de faire autrement, c'est qu'on peut customiser la vtable d'un objet en particulier. Pourquoi pas, mais en pratique c'est pas vraiment utile à mon avis.



                            Hum, c'est sûrement inutile si on ne veut pas aller trop loin, maiiiis... j'essaie justement de chercher ce en quoi ça pourrait être utile.

                            :o j'ai pas trouvé du temps pour continuer à coder, je vais tâcher de refaire des codes plus sains la prochaine fois.

                            Sinon @QuentinC 2 : :) votre avant dernier post est assez intéressant, je vais sûrement l'étudier en détail la prochaine fois que je code, justement.
                            • Partager sur Facebook
                            • Partager sur Twitter
                              2 mai 2011 à 0:00:20

                              Citation : QuentinC 2


                              Au fait ça m'amène à une question débile: pourquoi le C++ différencie-t-il delete et delete[] ? Quel est l'intérêt ?


                              delete fait plus que libérer la mémoire, il doit aussi appeler un(ou des) destructeur(s).
                              delete n'a aucun moyen de savoir si il est en présence d'un objet, ou d'un tableau d'objets.
                              C'est à nous de lui préciser en faisant l'appel soit avec [], soit sans.
                              • Partager sur Facebook
                              • Partager sur Twitter
                              Zeste de Savoir, le site qui en a dans le citron !
                                2 mai 2011 à 0:07:13

                                J'ajoute aussi que delete va invoquer les destructeurs dans l'ordre inverse.

                                Par contre, pourquoi syntaxiquement séparer les deux alors que le compilateur peut déterminer le type de l'objet à la compilation, aucune idée non plus.
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  2 mai 2011 à 0:09:44

                                  Citation : MaitreZur

                                  J'ajoute aussi que delete va invoquer les destructeurs dans l'ordre inverse.

                                  Par contre, pourquoi syntaxiquement séparer les deux alors que le compilateur peut déterminer le type de l'objet à la compilation, aucune idée non plus.



                                  ...peut-être simplement pour obliger le codeur à produire du code... lisible ?
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    2 mai 2011 à 0:12:17

                                    Dites, vouloir faire un lib en C qui peut faire de l'OO, c'est pas du masochisme?

                                    Enfin, moi j'fais du C justement pour me sortir la tête des langages objets que je bouffe tout les jours (au boulot ils bossent que sur Java et C#).

                                    Vous seriez pas des passionnés par hasard?
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      2 mai 2011 à 0:13:15

                                      Citation : heizmann

                                      Citation : MaitreZur

                                      J'ajoute aussi que delete va invoquer les destructeurs dans l'ordre inverse.

                                      Par contre, pourquoi syntaxiquement séparer les deux alors que le compilateur peut déterminer le type de l'objet à la compilation, aucune idée non plus.



                                      ...peut-être simplement pour obliger le codeur à produire du code... lisible ?


                                      Je ne vois pas l'information apportée par le fait qu'on libère un tableau d'objets ou un objet.

                                      L'élément important, c'est de lire qu'on libère la mémoire allouée.
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        2 mai 2011 à 0:17:46

                                        @MaitreZur je ne suis pas assez spécialiste sur les compilos pour le dire... :⁻/ des contraintes de l'époque peut-être ??...

                                        @quentin-fait-du-c : eh oui des passionnés :) faut lire plus haut (et avant aussi, y'a du code).

                                        C'est avant tout une question de pédagogie pour l'objet, et comprendre les mécanismes de la POO... sans toutefois être "ébahi" par la gestion de l'OO par les LOO
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          2 mai 2011 à 0:21:22

                                          Ouaip j'ai suivi tout le sujet, mais même si j'aime beaucoup comprendre les arcanes des trucs que je fais, le langage orienté objet je lui trouve beaucoup de complications pour peu de choses :(

                                          Et pis j'préfère clairement le bas que le haut niveau (j'ai fait du Windev une fois, berk !!), alors je vous laisse creuser hein ;)
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            2 mai 2011 à 0:25:02

                                            Citation : quentin-fait-du-c

                                            Ouaip j'ai suivi tout le sujet, mais même si j'aime beaucoup comprendre les arcanes des trucs que je fais, le langage orienté objet je lui trouve beaucoup de complications pour peu de choses :(

                                            Et pis j'préfère clairement le bas que le haut niveau (j'ai fait du Windev une fois, berk !!), alors je vous laisse creuser hein ;)



                                            Bah ^^ a priori, selon les dires de certains, l'objet du moment qu'on a : "encapsulation+héritage+polymorphisme", c'est bon :) après, on est un peu "libre" de définir les frontières de ce que l'on veut faire aussi. Par exemple en C++ on a les héritages multiples... D'autres langages préféreront implanter la notion d'interfaces ;) moi perso pouvoir le "recoder" sans mots-clés je trouve que ça a son intérêt pédagogique.
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              2 mai 2011 à 1:03:08

                                              En effet l'intérêt pédagogique n'est pas moindre ! J'ai du mal à me rendre compte, mais je pense qu'on parle de beaucoup de travail, beaucoup de RTFM et de RTFK&R :p

                                              Quand aux notions d'héritage multiples, d'interfaces, les codes que j'ai à faire ne sont pas assez pointus pour que j'en ai vraiment l'usage, donc je préfère m'amuser avec des pointeurs et des préprocesseurs !!
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                2 mai 2011 à 1:06:08

                                                Le préprocesseur est amusant :) mais attention il ne faut pas trop en abuser, et l'utiliser pour des usages comme ils sont préconisés dans les "codes bien écrits".
                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                  2 mai 2011 à 1:19:35

                                                  L'OO en C, c'est pas si compliqué à mettre en œuvre que ça avec GObject, je vous conseil de lire ça: (lien). D'ailleurs ça facilite grandement le déploiement d'applications Gtk/Gnome :)
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                  - Il y a un chemin vers chaque sommet, même le plus haut -
                                                    2 mai 2011 à 1:25:24

                                                    Citation : Sub-Zéro

                                                    L'OO en C, c'est pas si compliqué à mettre en œuvre que ça avec GObject, je vous conseil de lire ça: (lien). D'ailleurs ça facilite grandement le déploiement d'applications Gtk/Gnome :)



                                                    +1 ! :p mais GObject est dur à déchiffrer en ce qui concerne les mécanismes de l'implémentation POO en C... d'où l'idée de vouloir refaire une sorte de mini-GObject, pour le fun :-° (et la péda, mais je me fais vieux, je radodododote :magicien: )
                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                    Anonyme
                                                      2 mai 2011 à 9:28:23

                                                      Citation : Taurre

                                                      Citation : heizmann


                                                      mmhhh... :-° n'est-ce pas un peu dangereux ? Pas de contrôle de type (quoique... typeof marche avec gcc :( mais pas portable), et puis >_< il y a les effets de bords aussi...



                                                      Non, parce que le but est en fait de définir plusieurs fonctions prenant des types différents, mais d'uniformiser l'appel via une macro. Voici un exemple:

                                                      max.def




                                                      #ifndef MAX_DEF
                                                      #define MAX_DEF  1
                                                      
                                                      #define MAX_PROTOTYPE(TYPE) TYPE max_ ## TYPE (TYPE a, TYPE b)
                                                      
                                                      #define MAX_DEFINITION(TYPE) MAX_PROTOTYPE(TYPE) \
                                                      { \
                                                      	if ((a) > (b)) { \
                                                      		return (a); \
                                                      	} else { \
                                                      		return (b); \
                                                      	} \
                                                      }
                                                      
                                                      #define MAX(a, b, TYPE) max_ ## TYPE ((a), (b))
                                                      
                                                      #endif /* MAX_DEF */
                                                      



                                                      max.h




                                                      #ifndef MAX_H
                                                      #define MAX_H  1
                                                      
                                                      #include "max.def"
                                                      
                                                      MAX_PROTOTYPE (int);
                                                      MAX_PROTOTYPE (double);
                                                      
                                                      #endif /* MAX_H */
                                                      



                                                      max.c




                                                      #include "max.h"
                                                      
                                                      MAX_DEFINITION (int)
                                                      MAX_DEFINITION (double)
                                                      



                                                      main.c




                                                      #include <stdio.h>
                                                      #include <stdlib.h>
                                                      
                                                      #include "max.h"
                                                      
                                                      
                                                      int
                                                      main (void)
                                                      {
                                                      	printf ("%d\n", MAX (10, 20, int));
                                                      	printf ("%f\n", MAX (10.50, 10.40, double));
                                                      
                                                      	return EXIT_SUCCESS;
                                                      }
                                                      



                                                      Alors oui, je sais, cette fonction max est complètement inutile puisqu'elle pourrait-être implémentée via une simple macro, mais c'est juste pour l'exemple.

                                                      Le but est donc de masquer les appels à des fonctions différentes via la macro MAX et de simplifier les définitions des différentes fonctions via les macros MAX_PROTOTYPE et MAX_DEFINITION. Dans la réalité la validité des types est bel et bien assurée puisqu'on appel une fonction différente suivant le type des arguments ;)

                                                      EDIT: ne pas essayer avec un type composé de deux mots comme long long :-°



                                                      Il y a que moi qui trouve ça très très sale de devoir coder la fonction dans une macro ?
                                                      Je trouve ça pas propre et dangereux !

                                                      C'est très bien pour ces mini fonctions mais honnêtement ce n'est pas viable pour du code plus compliqué !
                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        2 mai 2011 à 14:07:22

                                                        Citation : ChristopheG


                                                        Il y a que moi qui trouve ça très très sale de devoir coder la fonction dans une macro ?
                                                        Je trouve ça pas propre et dangereux !



                                                        Cela dépend de ce que tu entends par sale :-°
                                                        Dans le mesure où la lecture est relativement facile et claire, je ne trouve pas que cela est "sale". D'autant qu'à mon sens il faut aussi voir le gain que cela apporte. Qu'est ce qui est mieux entre une multitude de définitions identiques et une seule définition confinée dans une macro? Pour moi c'est la macro sans aucun doute.

                                                        Sinon dangereux cela peut l'être oui, comme toute utilisation du préprocesseur. Maintenant, du moment qu'on est un tant soit peu consciencieux il y a moyen de s'en sortir sans problème :)

                                                        Citation : ChristopheG


                                                        C'est très bien pour ces mini fonctions mais honnêtement ce n'est pas viable pour du code plus compliqué !



                                                        Pourtant cela existe, par exemple les en-têtes queue.h et tree.h ;)
                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                          2 mai 2011 à 14:23:50

                                                          ^^ c'est un peu comme Jourdain quoi, on use du préprocesseur et de leurs macros en appelant des entêtes sans le savoir :-°
                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                          Anonyme
                                                            2 mai 2011 à 14:45:59

                                                            Non mais j'ai ma réponse, je doit être le seul ...

                                                            Citation : Taurre

                                                            Cela dépend de ce que tu entends par sale :-°
                                                            Dans le mesure où la lecture est relativement facile et claire, je ne trouve pas que cela est "sale". D'autant qu'à mon sens il faut aussi voir le gain que cela apporte. Qu'est ce qui est mieux entre une multitude de définitions identiques et une seule définition confinée dans une macro? Pour moi c'est la macro sans aucun doute.

                                                            Sinon dangereux cela peut l'être oui, comme toute utilisation du préprocesseur. Maintenant, du moment qu'on est un tant soit peu consciencieux il y a moyen de s'en sortir sans problème :)


                                                            J'ai dut mal a croire que cela soit souvent nécessaire de le faire. Tu a souvent besoin de faire 36 fonctions avec chaque types possibles et inimaginable ?
                                                            C'est très pratique, j'en convient, mais mise a part pour des bibliothèque générique, cela n'ai pas franchement courant de devoir utiliser beaucoup de types sur une même fonction. Et si tu veux pouvoir changer facilement avant compilation, tu peux utiliser un typedef. En général si tu réfléchis bien ton code, tu peux t'en passer (a part pour des cas comme des listes)

                                                            Citation : Taurre


                                                            Pourtant cela existe, par exemple les en-têtes queue.h et tree.h ;)



                                                            Que ça existe ne veut pas dire que c'est propre pour autant. On peut tout trouver, le problème n'est pas là. Il est probable que dans ce cas c'étais la solution la moins mauvaise, mais je ne trouve toujours pas ça terrible.



                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                              2 mai 2011 à 15:04:30

                                                              Bref... @ChristopheG :) alors, dites-nous comment on pourrait faire au lieu de dire ce qu'il ne faut pas faire...
                                                              • Partager sur Facebook
                                                              • Partager sur Twitter

                                                              La POO et le C

                                                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                              • Editeur
                                                              • Markdown