Programation par contraintes et class template

Euh ... comment on fait ?

L’auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour,

Dans l’objectif d’aider Paul et Topinambour, je me suis fait une classe CPolinome, pour les guider (s’ils me le demandent …). Et en accord avec ce que m’a conseillé Int21h, je l’ai faite en classe template, Mais j’ai une question.

Dans cette classe template, il y a l’opération division. Voulant appliquer une précondition (un assert en ce qui concerne la division par zéro!), je tests si l’un des coefficients du dividende est nul … mais avec un template, comme on fait ?

Pour la classe CRationel, j’ai une fonction membre "bool isNull () const;", faite sur mesure. Mais si j’instancie cette classe avec un float ou un double, cette fonction n’existe pas! Inversement, il est facile de faire une comparaison (m_coef == 0) pour un float ou un double, mais ça ne marche pas si les coefficient sont des CRationnal.

J’ai bien quelques idée, tous plus loufoques les une que les autres (il y a des fois, qu’est ce que je suis imaginatif ! 1: Encapsuler les float/double dans une classes avec toutes les opérations de float/double, plus une fonction membre isNull(). 2: Réaliser une fonction libre isNull(argumment), et la décliner avec tous les types d’argument qui pourrait être utilisée dans le tempate. 3: Utiliser une macro).

Mais vous, comment feriez-vous ?

(PS: J’espère que Paul et Topinambour ne viennent pas sur ce forum: Il faut bien qu’ils bossent un peu ;-) )

Édité par Dedeun

+0 -0

Cette réponse a aidé l’auteur du sujet

Tu peux regarder ce qu’est une classe de trait: https://cpp.developpez.com/faq/cpp/?page=Les-templates#Qu-est-ce-qu-une-classe-de-trait-Comment-l-utiliser

Et mettre comme comportement par défaut:

  • si std::is_floating_point est vrai (ou numeric_limits::is_integer est faux)
  • Utiliser une fonction membre isZero() ou une fonction libre isZero(T) uniquement si elles existent.

Mais après, est-ce vraiment utile ? Sur un flottant, le résultat sera NaN, ce qui est valide. Sur un entier, il y aura une erreur qui stoppera le programme.

+0 -0
Auteur du sujet

Merci Olybri et Jo link Noir pour vos réponses.

Olybri, tu me proposes de faire un opérateur == entre un rationnel et un entier. (Il est vrai que actuellement dans ma classe je n’ai pas l’opérateur ==, ou inférieur, supérieure, … c’est à faire!) Mais faire un == entre 2 types différents, est ce une bonne idée ?

A moins de faire un cast int —> rationnel, mais là aussi, je ne sais pas faire !

Jo, je ne connais pas les classes de trait. J’en ai entendu parlé. J’ai une toute petite idée de ce que c’est. Mais j’ai aucune idée de comment l’utilisée, je vais chercher … et revendrai vers vous si je blocks.

Par contre, je suis surpris par ta dernière remarque. Pour moi, diviser par zéro c’est la rupture d’un contrat mathématique (C’est un bug! Ce n’est pas un comportement nominal). Si je laisse s’accumuler les couches de soft sans à chaque fois vérifier ce contrat, je vais pouvoir se laisser construire mon application, et ne détecter les bug que très loin de son origine. Le jour où tombe cette rupture de contrat, loin dans le cycle de développement, il faudra remonté toute la files des appelants pour savoir que le bug, c’est pas la classe Rationnel, pas la classe Polynôme, … mais une classe au dessus. Pour moi, chaque classe doit vérifier ses contrats, (principalement pour aider au débug).

Y-aurait-il quelque chose que je n’aurai pas compris ?

Cordialement.

+0 -0

Cette réponse a aidé l’auteur du sujet

Perso, je déconseille aussi de te faire chier avec les NaN, les infs, et compagnie. C’est déjà hyper relou de le gérer au niveau des contrats des opérations simples, mais alors dès qu’on attaque des opérations plus compliquées, genre logarithme, exponentiel ou les racines, ça devient hyper relou de tout spécifier.

La division par 0, fais en un cas UB et c’est marre. Le développeur prendra un assert dans la bouche et c’est très bien comme ça.

Cette réponse a aidé l’auteur du sujet

J’ai lu l’énoncé sur le sujet cité, mais je ne comprends pas bien cette histoire de division. S’agit-il de la division d’un polynôme par un autre ou par un nombre ?
Et dans le premier cas, quel type de données est envisagé pour représenter le résultat ? Ou alors un certains nombres de divisions sont interdites car ne produisent pas un polynôme ?

Et quelle division par zéro veux-tu empêcher ? Les cas où le polynôme dénominateur finirait par s’évaluer à 0 quand appliqué à une variable ?
Tu souhaites donc identifier les racines du dénominateur pour prévenir ces cas ?

Auteur du sujet

Bonjour Ksass`Peuk, Bonjour Entwanne,

Merci de confirmer ce que je pensais, Ksass`Peuk.

Pour faire la division, entre 2 polynôme, j’avais fait la multiplication par l’inverse, … mais suite à ta remarque, je me rend compte que j’ai uniquement pris l’inverse des coefficients, pas l’inverse du polynôme. Donc je me posais la question du traitement de l’inverse d’un coefficient nul. Mais, c’est en effet, plus compliquer que ça ! Théoriquement, je ne sais pas faire, … Alors informatiquement non plus. Je vais donc supprimer mes fonction operator/= et operator/ . Merci de m’avoir rappeler à la réalité, Entwanne.

Cependant, je vais avoir la même difficulté, qui m’a amener ici, avec une fonction membre to_string (); de ma classe Rationnel (pour faire la sortie affichage des TU), je suis donc entrain de regarder les classes trait.

Pas facile d’utiliser les patterns (sauf dans la STL, où c’est fait pour!). Que c’est compliqué de faire simple.

Cordialement

Édité par Dedeun

+0 -0

Cette réponse a aidé l’auteur du sujet

je vais avoir la même difficulté, qui m’a amener ici, avec une fonction membre to_string();

Il suffit de faire une fonction to_string dans le même namespace et de jouer avec l’ADL. Cela peut se combiner avec des traits ou être une alternative.

J’ai fait un article sur le sujet il y a longtemps: https://jonathanpoelen.github.io/2013/07/utilisation-de-swap-et-des-fonctions-utilitaires-en-general/

Et un exemple plus poussé pour tester et utiliser les fonctions disponibles: https://jonathanpoelen.github.io/2020/04/sfinae/#jouer-avec-les-conversions-implicites (second code)

+0 -0
Auteur du sujet

Bonsoir,

Ca y est, j’ai compris ce que tu voulais me dire ! (bon d’accord, pas rapide … enfin on fait ce que l’on peut!)

Si je fais une fonction libre to_string (CRationnel cons &), ca revient surement au même ,que de développer operator« (CRationnel cons &). Et je viens de comprendre le message de Michel Billaud. C’est pour ça que j’ai besoin de geter().

Merci pour vos réponses.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte