[C] Et pourquoi pas de l'objet ?

Démarré par Morwenn, 09 Octobre 2011 à 23:55

0 Membres et 1 Invité sur ce sujet

09 Octobre 2011 à 23:55 Dernière édition: 10 Octobre 2011 à 00:19 par Guillaume
Ni questions, ni interrogations... Juste une petite histoire sur quelque chose qui m'est arrivé très récemment.

Je suis actuellement en école d'ingénieurs où j'étudie l'informatique. De manière tout à fait étonnante (ou pas), commence l'année en faisant du C. Et arrivé à un moment donné, on doit faire un projet en C. Ce projet doit utiliser des matrices, mais on nous laisse le choix de l'implémentation, soit.

Bien entendu, j'aurais aimé pouvoir utiliser du C++, faire une belle classe, faire des jolies méthodes de classes avec encapsulation des variables et tout et tout. Mais bon, on fait du C, donc pas question de ça... Soit, dans un accès de folie, j'ai quand même essayé de simuler une classe et ses méthodes. Petites explications ci-dessous^^




Une classe matrice en C

Puisqu'on a le choix de l'implémentation de la matrice, j'avais pensé à faire ça :


typedef struct
{
   size_t _height;
   size_t _width;
   double* _values;
} Matrix;


Bon, le choix de l'encapsulation à la manière C est déjà fait. Et donc, j'ai dû rajouter quelques méthodes get_height() et get_width() pour récupérer les valeurs. Jusque-là, rien de bien neuf. Sauf que voilà, j'ai toujours envie d'avoir mes méthodes de classes utilisables plus ou moins comme en C++.

Et là, l'illumination !
D'un coup, je décide de partir dans une espèce de code dégueulasse et qui risque de faire pousser des hurlements au professeur et provoquer en lui une vague de doute et d'incompréhension :ninja:




Première étape, on redéfinit notre structure en y ajoutant quelques pointeurs sur fonctions :


typedef struct
{
   size_t _width;
   size_t _height;
   double* _values;

   size_t (*get_height)(void);
   size_t (*get_width)(void);

} Matrix;


Juste la base en somme.
Maintenant, reste à utiliser ces pointeurs sur fonctions et quelques directives préprocesseur afin que tout ça ait l'air joli quand on l'utilise, mais si la vérité est plus triste quand on regarde l'implémentation. Voici donc le petit "constructeur" :


#define new_matrix(name, y, x) \
   Matrix name;                                     \
   name._height = y;                                \
   name._width = x;                                 \
   name._values = malloc(x * y * sizeof(double));   \
   \
   size_t UNIQUE(get_width, name##x##y)()           \
   {                                                \
       return name._width;                          \
   }                                                \
   name.get_width = &UNIQUE(get_width, name##x##y); \
   \
   size_t UNIQUE(get_height, name##x##y)()          \
   {                                                \
       return name._height;                         \
   }                                                \
   name.get_height = &UNIQUE(get_height, name##x##y);


Pour parvenir à ce résultat, la macro UNIQUE produira un nom de fonction composé du numéro de la ligne d'appel, ainsi que du nom et de la taille de la matrice déclarée. En gros, un nom supposé unique (à part en cas d'utilisation de plusieurs fichiers). Il ne reste plus qu'à attribuer l'adresse de cette fonction unique à un des pointeurs sur fonction de la structure et le tour est joué :D

Voici le code de la macro UNIQUE et des sous-macros utilisées :


#define BIND(x, y, ext) x##y##ext
#define UNIQUE_(name, x, ext) BIND(name##_, x, ext)
#define UNIQUE(name, ext) UNIQUE_(name, __LINE__, ext)



Sensiblement immonde me direz-vous, mais tellement agréable à utiliser après^^


int main()
{
   new_matrix(M, 5, 6);
   new_matrix(N, 2, 3); new_matrix(P, 5, 4);

   size_t a = P.get_height();

   printf("%d ", M.get_width()); printf("%d\n", M.get_height());
   printf("%d\n", N.get_width());

   return EXIT_SUCCESS;
}


Bref, je m'étonne toujours de ce qu'on peut faire en C. Probablement pas toujours pour le meilleur x)

10 Octobre 2011 à 00:16 #1 Dernière édition: 10 Octobre 2011 à 00:19 par Guillaume
Argh non tu n'as rien compris, et si Kernighan ou Ritchie te voyaient ils te donneraient une fessée.

Oui, il est possible de faire "de l'objet en C" via des truchements comme ceux que tu as présentés. Il est aussi possible d'écrire un interpréteur de langage fonctionnel (appellons le MorwennLambda) et de faire ton projet en écrivant des scripts MorwennLambda. Après tout, pourquoi pas ? Tu réponds à la question, ton projet fais ce qu'il est censé faire, et tu démontres une connaissance du langage bcp plus approfondie que celle de tous tes camarades.
D'ailleurs, si tu regardes ce que produit un compilateur C++, tu réaliseras qu'au final il fait plus ou moins ce que tu as écris.

Pourtant, c'est être complètement à côté de la plaque. Le C est un langage avec ses propres idiomes, sa propre philosophie, etc. qui est fait pour répondre à une certaine catégories de problèmes. Là tu outrepasses tout ça (en faisant du code super moche au passage- ta macro unique me donne envie de recracher tous mes repas des 3 dernières semaines) alors que si tu allais dans le sens du C, tout irait beaucoup mieux.

Oui, on peut enfoncer un clou avec un préservatif, mais c'est pas le meilleur moyen.

EDIT: sans compter qu'au final tu as un truc super bâtard orienté objet mais pas vraiment. Quid de l'héritage? Des variables privées vs. protégées vs. public? Des classes amies? Du polymorphisme?
Au passage tu n'es pas le premier à tenter ça, c'est un petit exercice intéressant en soi mais qui relève du blasphème pour du code de production. (lien)

Citation de: Morwenn le 09 Octobre 2011 à 23:55
Bref, je m'étonne toujours de ce qu'on peut faire en C. Probablement pas toujours pour le meilleur x)

Ah ben oui ça c'est sûr, tu peux faire ce que tu veux et sa grand mère en C ^^

Citation de: Guillaume le 10 Octobre 2011 à 00:16
Pourtant, c'est être complètement à côté de la plaque. Le C est un langage avec ses propres idiomes, sa propre philosophie, etc. qui est fait pour répondre à une certaine catégories de problèmes.

Oui, c'est aussi ce que j'aimerais répondre à mon prof de temps à autres quand il demande de faire de la programmation générique en C de manière plus ou moins illisible (Quoique le C1X pourra y apporter une solution un brin plus élégante, mais je le soupçonne de ne jamais avoir utilisé le C99, donc bon...)^^

Donc bon, quand on voit qu'il nous demande de faire de plus en plus de trucs pour lesquels ne sont pas faits le C, j'en venais à vrai dire à me demander ce que ça donnerait si on poussait plus loin encore ce principe... Ne t'en fais donc pas, je suis tout à fait conscient des idiomes du C lorsque je passe au-dessus (à vrai dire, pour certains trucs demandés, je meurs d'envie d'utiliser du C++, mais on n'a pas le droit).

Ton lien est intéressant en tout cas, le gars a dû y passer pas mal de temps, c'est impressionnant :blink:

Je viens de réaliser que mon post semble sans doute très critique envers ce que tu as fait ^^ En fait pas du tout, c'est un très bon exercice de style et c'est marrant et instructif de voir comment on peut casser les conventions des langages.

Juste que si j'étais prof et qu'un élève me rendait un devoir comme ça (ou que l'un de mes employés écrivait du code de production comme ça), ça passerait pas :D Après si le prof vous demande de faire du n'importe quoi en C, c'est de sa faute :ninja:

Bon sinon à chaque fois que je relis ça...

#define BIND(x, y, ext) x##y##ext
#define UNIQUE_(name, x, ext) BIND(name##_, x, ext)
#define UNIQUE(name, ext) UNIQUE_(name, __LINE__, ext)



Bah, moyennant casts, tu peux faire bcp de choses :
- faire de l'héritage en c
- casser l'encapsulation en c++
- ...

Tu peux aller voir les tutos sur developpez.com si ça t'intéresse. :)
Après, tu peux aussi voir le projet Gnome, avec gLib et gObject qui permettent l'oo en C. Sans oublier Vala, suite logique puisqu'il s'agit d'un langage oo qui compile en C/gObject.

Sinon, je suis d'accord avec Guillaume, ça devrait rester du joujou/expérimentation/apprentissage ^_^
Marre des pavés ? Marchez dans la boue!
ハハ、あなたは私の罠に落ちた!

Je ne savais pas qu'il y avait autant de projets pour faire de la prog objet en C, je pensais aussi que ça restait toujours au stade de la simple expérimentation, faudra que je jette un coup d'œil :P

Citation de: Guillaume le 10 Octobre 2011 à 00:31
Bon sinon à chaque fois que je relis ça...

#define BIND(x, y, ext) x##y##ext
#define UNIQUE_(name, x, ext) BIND(name##_, x, ext)
#define UNIQUE(name, ext) UNIQUE_(name, __LINE__, ext)




Ouais, à priori, sans avoir tous ces niveaux de macros les uns au-dessus des autres, tout ce que va me faire mon préprocesseur, c'est par exemple pour UNIQUE(nom, fin) me ressortir nom____LINE__fin au lieu de nom_5fin si __LINE__ correspond à la ligne 5. Ça fait partie des trucs les moins instinctifs je trouve.

Citation de: Morwenn le 10 Octobre 2011 à 00:39
Je ne savais pas qu'il y avait autant de projets pour faire de la prog objet en C, je pensais aussi que ça restait toujours au stade de la simple expérimentation, faudra que je jette un coup d'œil :P


http://en.wikipedia.org/wiki/Objective-C

:P

Ah oui, mais là, on change carrément de langage pour passer à la prog objet :P

En parlant des évolutions du C, j'aurais bien envie d'essayer le D un de ces jours, mais son problème de bibliothèque standard semble être quelque peu gênant quand même :/

Non justement, l'objective-C est du C avec une surcouche qui ajoute du messaging et qui fait du binding dynamique en runtime (ouhlà Papy39 va nous faire une attaque cardiaque en lisant ça!)

Jamais testé le D, mais il parait que c'est sympa. Si je ne m'abuse Kenta Cho a fait tout ses jeux avec.

Actuellement je bosse plus ou moins sur du code source de VLC, qui est écrit en C et dont la partie libVLCcore est en "orienté-objet".

Plus d'infos sur le wiki officiel.

(la doc reste très générale je préviens).

Le langage C n'est vraisemblablement pas conçu pour être facilement adapté au paradigme objet. Ceci dit il est tout à fait possible de détourner de nombreuses choses pour arriver à ses fins et ce n'est clairement pas toujours la meilleure solution...

Bref comme l'ont dit les autres, ok c'est fun mais rend pas ça à ton prof  :P

(et pour les casts dégueulasses, demander à chris  :ninja:)

Citation
vlc_object_create() - create an object and set its refcount to 0
vlc_object_destroy() - de-allocate an object iff its refcount = 0
vlc_object_attach() - bidirectionally link an object with its parent
vlc_object_detach() - remove all links between an object and its parent


:o
On dirait qu'il y a des gens qui ne comprennent pas que chaque langage à ses avantages et inconvénients, et que pousser aux limites c'est juste faire du code impossible à relire :o
Citation
Ash Nazg Durbatulùk, Ash Nazg Gimbatul,
Ash Nazg Thrakatulùk agh bruzum-ishi krimpatul.
The fellowship of the Ring - J.R.R. Tolkien

Citation de: BenObiWan le 10 Octobre 2011 à 11:47pousser aux limites c'est juste faire du code impossible à relire :o

Mais tellement fun à écrire ! :D Ça doit bien compter pour quelque chose?

Bah pour faire des petits trucs comme Morwenn pourquoi pas, mais pour un projet comme VLC... WTF?
Citation
Ash Nazg Durbatulùk, Ash Nazg Gimbatul,
Ash Nazg Thrakatulùk agh bruzum-ishi krimpatul.
The fellowship of the Ring - J.R.R. Tolkien

S'il était écrit en Java, il planterait, ralentirait, fuiterait, ... Bref, je préfère C !
:trollface:
Gnome, le noyau Linux, ... Et beaucoup de librairies sont écrites en C. Pourquoi pas le C++, c'est un autre débat :P

@Morwenn >
Le D, ouais, moi aussi j'ai envie d'essayer quelques petit programmes. Pour le problèmes des 2 libs standards incompatibles, il me semblait que c'était résolu depuis D 2.0, non ? :o
Sinon, c'est clair que ça a freiné l'adoption du langage, malheureusement :/
Marre des pavés ? Marchez dans la boue!
ハハ、あなたは私の罠に落ちた!

Citation de: Wouf le 10 Octobre 2011 à 13:54
Gnome, le noyau Linux, ... Et beaucoup de librairies sont écrites en C. Pourquoi pas le C++, c'est un autre débat :P
Parce que le C++, dès que c'est gros, ça devient souvent horrible à debuguer. En tout cas, beaucoup plus bouffeur de temps qu'en Java par exemple (ou même en C ? Pas essayé.).

Citation de: Sam101 le 10 Octobre 2011 à 17:28
Parce que le C++, dès que c'est gros, ça devient souvent horrible à debuguer. En tout cas, beaucoup plus bouffeur de temps qu'en Java par exemple (ou même en C ? Pas essayé.).

Mouais, si tu as un bon design, que tu codes de manière défensive (assert et tralalala), le C++ n'est vraiment pas bien pire que le Java ou autre langage. Oui la gestion de la mémoire peut donner du fil à retordre, mais globalement ça dépend juste de la capacité des développeurs à faire attention à ce qu'ils font.

Citation de: Wouf le 10 Octobre 2011 à 13:54
@Morwenn >
Le D, ouais, moi aussi j'ai envie d'essayer quelques petit programmes. Pour le problèmes des 2 libs standards incompatibles, il me semblait que c'était résolu depuis D 2.0, non ? :o
Sinon, c'est clair que ça a freiné l'adoption du langage, malheureusement :/

J'ai entendu dire il me semble qu'ils ont implémenté un système permettant au langage d'utiliser plusieurs librairies standard en même temps (je ne sais pas comment c'était et comment c'est géré). Mais bon, c'est con d'en arriver là pour ça, quoi x)

@Sam : Avec la programmation défensive, on peut en effet faire des debugs assez efficaces. D'autant plus depuis qu'ils ont rajouté les assertions statiques (même si c'était déjà simulable avant) :)

Citation de: Guillaume le 10 Octobre 2011 à 19:23
mais globalement ça dépend juste de la capacité des développeurs à faire attention à ce qu'ils font.

Comme avec tout langage. C'est surtout le développeur qui compte.
Citation
Ash Nazg Durbatulùk, Ash Nazg Gimbatul,
Ash Nazg Thrakatulùk agh bruzum-ishi krimpatul.
The fellowship of the Ring - J.R.R. Tolkien

Citation de: BenObiWan le 10 Octobre 2011 à 21:16
Citation de: Guillaume le 10 Octobre 2011 à 19:23
mais globalement ça dépend juste de la capacité des développeurs à faire attention à ce qu'ils font.

Comme avec tout langage. C'est surtout le développeur qui compte.

+1

10 Octobre 2011 à 22:18 #20 Dernière édition: 10 Octobre 2011 à 22:23 par Guillaume
Citation de: Binbin le 10 Octobre 2011 à 22:00
+1

Faux. Il faut faire un truc du genre:

mutex_write_lock(post);
assert(post!=NULL);
assert(post.points<(MAX_INT-1));
post.points+=1;
mutex_unlock(post);


Sinon on peut utiliser des structures évolués comme des AtomicInteger ;)
Citation
Ash Nazg Durbatulùk, Ash Nazg Gimbatul,
Ash Nazg Thrakatulùk agh bruzum-ishi krimpatul.
The fellowship of the Ring - J.R.R. Tolkien


C'est marrant comme expérience, "qui sera le premier à comprendre que je parle de Java et crachera dessus?" :D
Citation
Ash Nazg Durbatulùk, Ash Nazg Gimbatul,
Ash Nazg Thrakatulùk agh bruzum-ishi krimpatul.
The fellowship of the Ring - J.R.R. Tolkien