IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Interdire les conversions implicites


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    5
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 5
    Par défaut Interdire les conversions implicites
    Bonjour,

    Est-ce que quelqu'un saurait comment empêcher toute conversion implicite en C++ (et ainsi faire en sorte de devoir expliciter toute les conversions nécessaires).

    Je préférerai vraiment le mentionner en utilisant le langage C++, mai SI jamais s'était impossible est-ce que quelqu'un connaît un paramètre à passer à g++ pour qu'il signale une erreur à chaque fois qu'une conversion implicite est utilisée?

    (Si quelqu'un se pose la question du pourquoi je veux faire ça, c'est parce que je voudrais retrouver un semblant de typage fort dont j'ai l'habitude dans d'autres langages)

    Remarque: je cherche vraiment à empêcher toute conversion implicite, et non pas à me "débrouiller" pour obtenir un semblant de ce que je veux faire; par exemple en redéfinissant des classes reproduisant les types de base... j'ai essayé, c'est chiant, lourd, ça complique le code inutilement et ça ne fait pas exactement ce que je veux (à moins que je puisse encore ajouter des contraintes en alourdissant encore plus le code...)

    Merci.

  2. #2
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Tu veux interdire quoi exactement ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    double d = 1; //?
    Base *b = new Derivee; // ?
    Tu peux nous donner l'exemple d'autre langage auquel tu fais allusion, afin qu'on voit mieux ce que tu as en tête ? Même si je ne pense pas que ce que tu veux soit possible, et que je reste à convaincre sur le fait que ce soit souhaitable
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  3. #3
    boli_971
    Invité(e)
    Par défaut
    Salut,

    Quand tu invoque g++ tu peut ajouter -Wconversion pour être averti des conversion implicites.

  4. #4
    Membre à l'essai
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    5
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 5
    Par défaut
    Merci beaucoup boli_971, ça semble correspondre à ce que je cherche et j'irai voir la doc officielle de g++ pour essayer d'en savoir plus.
    Par exemple en faisant des petits exemples simples, Wconversion semble laissé passer des conversions unsigned int vers int et inversement.

    Même si j'aurais préféré un truc qui soit propre au langage et pas au compilateur, au cas où je serais amené à utiliser un autre compilateur.

    Pour répondre à JolyLoic, ce que je cherche est effectivement d'empêcher toute conversion implicite.
    L'intérêt je suis pas le seul à le voir et c'est tout à fait possible, c'est l'intérêt du "typage fort" et tu peux chercher sur le net pour avoir des information détaillées. Attention il n'y a pas qu'une seule notion de typage fort; il n'y a pas des groupes de langage qui font du typage faible, d'autres du typage fort et d'autre pas de typage du tout, chaque langage à son propre degré de typage avec ses propres contraintes. D'ailleurs je me demande si ce qui est le plus difficile n'est pas justement le fait de faire autre chose que du typage fort. Un compilateur C++ doit par exemple se poser systématiquement la question de qul est le type de résultat qu'une expression doit avoir et de quelle façon convertir implicitement toute valeurs utilisée dans l'expression; le système de surcharge de fonction complique encore plus se problème. Un langage qui fait du typage très fort n'a pas à se poser la question, soit les types correspondent exactement à ce qu'il faut et alors pas de problème, soit ça correspond pas exactement (même à un type synonyme si on veut pousser le truc jusqu'au bout) et alors il y a une erreur et on ne compile pas.

    Par exemple on pourrait se demander quel est le sens (sémantique) à donner à une conversion "unsigned int" vers "int" notamment quand la valeur de l'entier non signé ne fait pas partie de la plage de valeurs des entiers signés. L'inverse aussi me pose problème: que signifie une conversion de '-2' ou '2.6' vers un entier non signé? La question n'est pas d'empêcher complètement toute conversion, mais de forcer le programmeur à expliciter toute les conversions nécessaires pour lui donner une chance de se poser la question du sens de ce qu'il est en train de faire. Si le système de cast lui convient autant les utilisez (et il faudrait alors quand même savoir ce qu'un cast "unsigned int" vers "int" fait exactement, est-ce que tout le monde s'est vraiment posé la question, en particulier les débutants?) , mais il peut aussi vouloir définir ses propres fonctions de conversion adaptées au programme qu'il est en train de développer.

    Voilà typiquement le genre de programme C++ que je ne voudrais pas être capable de compiler:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    int myadd(float af, int ai) { return af+ai; }
     
    int main () {
      int i=-2;
      unsigned int ui=i;
      int si= ui;
      float f=i;
      float anof=myadd(5, 6.2);
      return 0;
    }
    Idéalement j'aimerais aussi interdire "double d = 1;" mais j'ai l'impression que ça ne vas pas être possible en C++ vu qu'il n'a pas de notation séparée pour les entier de type double ou float. D'ailleurs C++ est-il capable de faire une différence entre le '1' entier signé et le '1' entier non signé par exemple.

    Pour te donner un exemple d'un autre langage que j'utilise régulièrement voici des exemples en ocaml. Ocaml est un peu particulier vu qu'il fait du typage fort mais qu'on est pas obligé de précisé les types puisqu'il est capable d'inférer lui-même les types les plus généraux utilisables avec la fonction que tu viens de définir (il est cependant possible de préciser explicitement des types pour limiter la généralisation, mais ce système permet de faire de la généricité directement), mais je ne vais pas donner d'exemple de généralisation vu que c'est pas le sujet qui est abordé dans ce topic.

    Je te donne un extrait obtenu à partir de la boucle interactive (plus rapide pour te donner un exemple que de compiler un fichier). Note la différence entre l'écriture '2' qui est reconnue comme un int et '2.' qui est reconnu comme un float. De la même façon '+' est une addition sur des entiers alors que '+.' est l'addition sur des float. Les messages d'erreurs peuvent paraître un peu imprécis, mais en fait c'est parce que la boucle interactive souligne la partie du code concernée et que le copier/coller perd ce soulignement. (Attention la balise CODE de ce forum semble colorier le texte à la manière de C, en fait les commandes importantes sont celles qui commencent par # (c'est le "prompt" en quelque sorte) puisque que c'est là que j'écris ce qui est ensuite interpréter par la boucle interactive... les commentaires renvoyés par la boucle interactive sont donc en fait ce tout ce qui ne commence pas par #, le contraire de C en somme)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    # 2;;       
    - : int = 2 
    # 2.;;
    - : float = 2.
    # 0. + 2;;
    Error: This expression has type float but is here used with type int
    # 0 + 2;;
    - : int = 2
    # 0. +. 2;;
    Error: This expression has type int but is here used with type float
    # 0. +. 2.;;
    - : float = 2.
    # let f x y = x + y;;
    val f : int -> int -> int = <fun>
    # let g x y = x +. y;;
    val g : float -> float -> float = <fun>
    # f 2. 3.;;
    Error: This expression has type float but is here used with type int
    # g 2. 3.;;
    - : float = 5.
    Évidemment ce sont là des exemples très simples mais c'est le même principe qui s'applique partout. Il y a évidemment des fonctions standards de conversion des types de bases vers d'autres.

    Tu me dira peut-être "quel intérêt?" vu que le programmeur n'a qu'à s'imposer lui-même de développer son programme de façon à ne pas provoquer de conversions implicites... Si nous étions capables de programmer sans faire d'erreur et de ne nous imposer des règles de bonne programmation sans jamais en dévier ça se saurait. Le typage fort n'empêche évidemment pas de faire des erreurs mais il a le mérite d'être un moyen complètement automatique (il n'y a en a pas tant que ça) de s'éviter quelques problèmes et de forcer le programmeur à se poser quelques questions non intéressante, pas inutile, et pas forcément si trivial que ça en a l'air du style "quel sens je veux donner dans mon programme à la conversion d'un float vers un int".
    Par exemple en C++ l'expression "a/2" n'aura pas le même sens suivant que a sera déclaré comme un float ou comme un int. Tant mieux si le programmeur s'assure systématiquement avant d'écrire une division (et plus généralement n'importe quel expression et appel de fonction) de bien connaître le type de "a" et qu'alors l'expression à bien le sens qu'il souhaite; et que plus tard (peut-être même beaucoup plus tard) personne ne vienne jamais changer la déclaration de a pour le faire passer d'un int à un float par exemple (une personne - dangereuse, à envoyer sur le bûcher immédiatement- qui ferait ça n'ira pas forcément regarder toutes les utilisations de "a" qui ont pu être faite et corriger correctement tout ce qui a besoin de l'être, imagine que ce ce soit une librairie).

  5. #5
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par naadhicar Voir le message
    Merci beaucoup boli_971, ça semble correspondre à ce que je cherche et j'irai voir la doc officielle de g++ pour essayer d'en savoir plus.
    -Wconversion
    Warn for implicit conversions that may alter a value. This includes conversions between real and integer, like abs (x) when x is double; conversions between signed and unsigned, like unsigned ui = -1; and conversions to smaller types, like sqrtf (M_PI). Do not warn for explicit casts like abs ((int) x) and ui = (unsigned) -1, or if the value is not changed by the conversion like in abs (2.0). Warnings about conversions between signed and unsigned integers can be disabled by using -Wno-sign-conversion.

    For C++, also warn for conversions between NULL and non-pointer types; confusing overload resolution for user-defined conversions; and conversions that will never use a type conversion operator: conversions to void, the same type, a base class or a reference to them. Warnings about conversions between signed and unsigned integers are disabled by default in C++ unless -Wsign-conversion is explicitly enabled.
    Comme tu le vois, c'est loin de supprimer toute conversion.
    Citation Envoyé par naadhicar Voir le message
    Pour répondre à JolyLoic, ce que je cherche est effectivement d'empêcher toute conversion implicite.
    L'intérêt je suis pas le seul à le voir et c'est tout à fait possible, c'est l'intérêt du "typage fort" et tu peux chercher sur le net pour avoir des information détaillées. Attention il n'y a pas qu'une seule notion de typage fort; il n'y a pas des groupes de langage qui font du typage faible, d'autres du typage fort et d'autre pas de typage du tout, chaque langage à son propre degré de typage avec ses propres contraintes. D'ailleurs je me demande si ce qui est le plus difficile n'est pas justement le fait de faire autre chose que du typage fort. Un compilateur C++ doit par exemple se poser systématiquement la question de qul est le type de résultat qu'une expression doit avoir et de quelle façon convertir implicitement toute valeurs utilisée dans l'expression; le système de surcharge de fonction complique encore plus se problème. Un langage qui fait du typage très fort n'a pas à se poser la question, soit les types correspondent exactement à ce qu'il faut et alors pas de problème, soit ça correspond pas exactement (même à un type synonyme si on veut pousser le truc jusqu'au bout) et alors il y a une erreur et on ne compile pas.
    Tout à fait. Des règles de typage absolues sont bien plus simples à mettre en place que des règles plus souples (un chapitre entier de la norme C++ est consacré au sujet). D'où la question qui peut se poser : Pourquoi avoir mis en place ces règles plus souples ? Et bien, tout simplement parce qu'elles n'apportent pas que des inconvénients, comme tu sembles l'indiquer, mais aussi des avantages. Ensuite, la question en l'occurrence est de savoir exactement où s'arrêter. Il y a des points où effectivement, le C++ (obligé de suivre le C souvent) me semble être allé un peu trop loin. Il y a d'autres points où un typage plus strict me semblerait une abomination. Par exemple, mon exemple avec pointeur sur classe de base et pointeur sur classe dérivée me semble un cas où une conversion est positive. J'ai aussi en mémoire une mauvaise expérience en Pascal où il était impossible d'écrire une fonction qui puisse prendre en paramètre un tableau d'un nombre variable de cases, car chaque taille de tableau faisait un type différent incompatible. Certes, c'était plus propre sur le plan théorique, mais sur le plan pratique, ça rendait le langage inutilisable.
    Citation Envoyé par naadhicar Voir le message
    Par exemple on pourrait se demander quel est le sens (sémantique) à donner à une conversion "unsigned int" vers "int" notamment quand la valeur de l'entier non signé ne fait pas partie de la plage de valeurs des entiers signés.
    Sauf que si la valeur ne fait pas partie de cet intervalle, elle ne pose pas problème, et peut être très utile... A moins que tu ne veuille avoir pour les unsigned un opérateur- qui retourne 0 pour a-b si b>a ? Il s'agit de vrais problèmes posé par les limitations de l'informatique où l'on ne peut pas modéliser correctement un élément de N ou Z. Et ils n'ont pas à mon sens de solution parfaite. Tout est alors affaire de compromis.
    Citation Envoyé par naadhicar Voir le message
    L'inverse aussi me pose problème: que signifie une conversion de '-2' ou '2.6' vers un entier non signé? La question n'est pas d'empêcher complètement toute conversion, mais de forcer le programmeur à expliciter toute les conversions nécessaires pour lui donner une chance de se poser la question du sens de ce qu'il est en train de faire.
    Forcer un programmateur à écrire du code verbeux pour lui faire se poser une question ne marche probablement que si ce code verbeux est rare. S'il devient courant, il n'y a plus de questions. C'est comme les warnings. Un programme qui quand on le compile produit 3 warnings, on les étudies. S'il en produit 500...

    Une option plus intéressante serait un système qui vérifie si la conversion numérique produit effectivement une perte d'information ou pas. La question (outre les performances) devient alors comment gérer les cas où une perte a eu lieu.
    Citation Envoyé par naadhicar Voir le message
    Idéalement j'aimerais aussi interdire "double d = 1;" mais j'ai l'impression que ça ne vas pas être possible en C++ vu qu'il n'a pas de notation séparée pour les entier de type double ou float.
    Je n'ai pas trop compris cette phrase. De plus 1, 1., 1.f, 1u, 1l, 1ul sont 6 valeurs différentes.
    Citation Envoyé par naadhicar Voir le message
    Pour te donner un exemple d'un autre langage que j'utilise régulièrement voici des exemples en ocaml.
    [...]
    Tu me dira peut-être "quel intérêt?" vu que le programmeur n'a qu'à s'imposer lui-même de développer son programme de façon à ne pas provoquer de conversions implicites... Si nous étions capables de programmer sans faire d'erreur et de ne nous imposer des règles de bonne programmation sans jamais en dévier ça se saurait. Le typage fort n'empêche évidemment pas de faire des erreurs mais il a le mérite d'être un moyen complètement automatique (il n'y a en a pas tant que ça) de s'éviter quelques problèmes et de forcer le programmeur à se poser quelques questions non intéressante, pas inutile, et pas forcément si trivial que ça en a l'air du style "quel sens je veux donner dans mon programme à la conversion d'un float vers un int".
    J'ai l'impression que tu te focalises sur les conversions d'un type numérique en un autre, ce ne sont pas les seules conversions qui existent, même si ce sont probablement les plus controversées. Mais je suis heureux que tu ne veuilles pas imposer d'expliciter toutes les conversions
    Citation Envoyé par naadhicar Voir le message
    Par exemple en C++ l'expression "a/2" n'aura pas le même sens suivant que a sera déclaré comme un float ou comme un int. Tant mieux si le programmeur s'assure systématiquement avant d'écrire une division (et plus généralement n'importe quel expression et appel de fonction) de bien connaître le type de "a" et qu'alors l'expression à bien le sens qu'il souhaite;
    Cette surcharge de l'opérateur / pour les entiers pose en effet souvent problème aux débutants. Mais là, ce que tu voudrais, ce n'est pas une non conversion (après tout, il n'y a aucune conversion en C++ quand on écrit 1/2) mais une non surcharge des opérateurs.
    Citation Envoyé par naadhicar Voir le message
    et que plus tard (peut-être même beaucoup plus tard) personne ne vienne jamais changer la déclaration de a pour le faire passer d'un int à un float par exemple (une personne - dangereuse, à envoyer sur le bûcher immédiatement- qui ferait ça n'ira pas forcément regarder toutes les utilisations de "a" qui ont pu être faite et corriger correctement tout ce qui a besoin de l'être, imagine que ce ce soit une librairie).
    Je trouve cet argument un peu tiré par les cheveux. J'ai du mal en effet à imaginer un programme raisonnable où un utilisateur voudrait changer un int en float. Ces types sont vraiment différents, on ne les utilise pas dans les mêmes buts généralement.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    5
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 5
    Par défaut
    Effectivement j'étais en train de regarder ce que faisais Wconversions et me rendre compte qu'elle était assez limité.
    Peut-être tout simplement que le langage C++ n'est pas du tout adapté à ce que je veux faire.

    Par ailleurs je suis assez d'accord avec toi sur l'histoire des conversions de pointeur. Personnellement j'ai tendance à interpréter simplement (mais peut-être justement beaucoup trop simplement) un pointeur comme une simple adresse vers une case mémoire et du coup je ne vois pas bien pourquoi il serait nécessaire de préciser systématiquement le type de la valeur qui est pointée. Personnellement j'aurai naturellement tendance en C++ à vouloir avoir l'entière liberté d'interpréter le code binaire présent à l'adresse indiquée comme je le veux (un int à un moment, un unsigned in à une autre... selon ce que je veux faire), et si j'avais à défnir mon propre langage la seule chose que je permettrai serait de préciser la taille de l'objet pointé (est-ce qu'on considère les deux premières bits, les 32 bits, les 238 bits s'étalant sur plusieurs mots mémoires...) et je donnerai la possibilité de pouvoir modifier les références pour qu'elle puisse être utilisée un peu à la manière des pointeurs (mais sans notion d'adresse mise à disposition du programmeur) et pour le coup référencer une valeur d'un type bien précis qu'il faudrait éventuellement convertir explicitement si besoin.

    D'ailleurs à la base, avant de regarder la question de plus près, j'étais persuadé que les casts de C++ consistait simplement à réinterpréter différemment (suivant le type mentionné) le code binaire en le conservant tel quel. Et puis je me suis aperçu que finalement que pas du tout, et que ça faisait des conversions mathématique pas si triviales que ça, par exemple si je me trompe pas un cast vers un int de "-2.5" prend la partie entière de la valeur absolue puis la transforme en nombre négatif pour obtenir -2 (alors que la partie entière de -2.5 est -3). Quand on le sait on fait avec, mais je suis pas sûr que tout le monde aurait naturellement tendance à l'interpréter de cette façon sans s'être renseigné avant. Si un cast explicite était obligatoire on a plus de chances de se poser la question que si on ne rend pas bien compte qu'une conversion vas être automatiquement ajoutée par le compilateur.

    Tout ceci ne concerne évidemment pas les experts en C++ qui connaissent tout ça sur le bout des doigts. Mais tu l'auras compris je suis très très très loin d'être expert en C++. Et si je veux m'imposer des contraintes c'est justement pour augmenter les chances de détecter quelques erreurs que je pourrais faire sans m'en rendre compte. Donc même si l'option Wconversions est loin d'être parfaite c'est toujours ça de pris.

    En plus je trouve qu'il se passe des choses bizarres parfois, par exemple si j'écris "unsigned int ui=-2.5" g++ me met un warning (sans ajouter un quelconque -W...) pour une histoire de débordement de conversion implicite (je sais pas ce que ça veut dire, mais à la limite personnellement ça m'arrange plutôt qu'il me dise qu'il y a un problème), par contre si j'écris "unsigned int ui=-2" ou 'int i=-2.5" là il n'y a plus de warning alors que je vois pas pourquoi il peut convertir -2.5 en int ou -2 en unsigned int sans rien dire mais qu'il estime nécessaire de faire un warning quand on veut convertir -2.5 en unsigned int. Mais il doit sans doute y avoir une très bonne raison qui m'échappe.

    Sinon je crois pas du tout que d'imposer que les conversions amènent nécessairement à du code beaucoup plus verbeux. C'est peut-être que j'ai l'habitude d'écrire en ocaml où il n'y a pas de conversion implicite et que le code ne devient pas du tout verbeux. Si ton code devient très verbeux c'est probablement parce que au départ il y avait des conversions implicites dans tous les sens; et à mon avis dans ce cas il faudrait mieux se demander si toutes ces conversons sont bien judicieuses et s'il ne faudrait pas revoir tout ça pour qu'il y ait beaucoup de moins de conversions. Si je je me trompe pas à chaque fois qu'il y a conversion il y a toujours perte (ou risque de perte selon les cas) d'information. Et je crois pas qu'un compilateur puisse automatiquement repérer à la compilation quand il peut y avoir une perte d'information ou non, et il vas devoir ajouter presque systématiquement du code pour la conversion même si j'imagine qu'il doit être possible de programmer les choses pour éviter que ce code ne soit utilisé dans les cas où il n'est pas nécessaire. il n'y a peut être que le cas des constantes connues à la compilation qui pourrait être résolu.

    Sinon le fait qu'il y ait beaucoup de warning de me dérange pas, au contraire presque. Mais je dois dire que je n'utilise pas souvent -Wall parce que je me retrouve souvent avec des warning dont je ne sais pas forcément bien ce que ça veut dire exactement (par exemple du genre l'histoire de débordement de conversion implicite dont je parlais plus haut), et je n'ai pas l'intention de devenir expert en C++ pour comprendre la signification de chaque warning et savoir si tel warning devrait me conduire à modifier mon code ou tel autre non car c'est exactement ce que je voulais faire; en plus des fois je sais même pas comment il exactement il faudrait modifier le code pour que le warning disparaisse et je me retrouve à faire des modifications à tâton pour faire disparaître le warning en essayant de ne pas modifier la sémantique de mon programme.
    Par contre si je sais ce que veulent dire les warning ça me dérange pas qu'il y en ai plein, et à ce moment je les traite un par un. Cela-dit je vais quand même essayer dès le départ d'écrire un code qui va générer le moins de warning possible (par exemple en essayant de faire attention à ne pas faire de conversions implicites), et puis je n'attend pas non d'avoir écrit 100000 lignes avant de faire analyser le code.

    Je ne savais pas qu'il existait des notations 1, 1., 1.f, 1u, 1l, 1ul représentant des valeurs de types différents. Ça va s'en aucun doute m'être utile.

    Sinon je ne me focalise pas du tout sur les conversions numériques de base. C'est juste que c'est plus simple à expliquer et pour donner des exemples pas trop long. D'ailleurs j'ai plutôt utilisé des décalrations de variables dans mes exemples alors que ce qui va probablement le plus m'intéresser ce sont les conversions implicites lors des appels de fonctions. En plus quand on commence à utiliser l'aspect POO il y a des histoires de polymorphisme qui viennent se mêler et qui complique les choses. Par ailleurs avec les classes je connais déjà le mot clef explicit à ajouter à la déclaration des constructeurs pour éviter qu'ils ne soient appelés implicitement.

    Je ne trouve pas du tout l'argument de quelqu'un viendrait modifier ton programme, par exemple en modifiant un type par un autre, totalement tiré par les cheveux. En pratique un programme réel est quasi-systématiquement modifié par la suite par des personnes qui n'ont d'aucune façon participé au développement initial. Certains modules arrivent à survivre de très longues années comme ça. Cependant, comme je pense que je le faisais remarquer assez explicitement dans mon message j'espère qu'aucun développeur doté d'un minimum de conscience n'ira s'amuser à modifier un int en float.
    Mais j'ai malheureusement l'impression qu'il ne faut pas être trop optimiste sur ce genre de choses. Et je connais des patrons qui ne se collent pas eux-même au code qui n'auront pas de scrupule à vous expliquer le plus simplement du monde que pour ajouter telle ou telle fonctionnalité au programme il suffit d'utiliser un float plutôt qu'un int, ou toute autre modification de ce genre qui leur paraisse être aussi simple que de modifier trois lignes dans le code (et qui du coup ne jugeront pas opportun d'accorder plus de temps que nécessaire à cette modification). Par contre lorsque même patron s'apercevra que plus rien ne fonctionne correctement dans le logiciel ce sera de la faute du développeur qui n'a pas fait correctement son travail. Si le développeur en question a démissionné entre temps pour aller travailler dans une boîte qui lui semble plus sérieuse (ou pire le développeur était peut-être même un stagiaire, voire un sous-traitant dans un pays à bas coût) , ceux qui vont devoir refaire fonctionner le programme auront peut-être bien du mal à se rendre compte que c'est simplement parce qu'un int a été remplacé par un float dans une entête de fonction noyé dans une forêt de module. Et me dit pas que c'est tiré par les cheveux c'est quasiment du vécu.

    Ce genre d'erreur stupide n'est pas du tout tiré par les cheveux et n'est pas réservée au débutant. En 1996 on a fait explosé une Ariane 5 simplement en réutilisant un programme du projet d'Ariane 4 qui travaillait en 16bit alors qu'Ariane 5 fonctionne en 64bit. Et personne ne s'est rendu de rien jusqu'à ce que la fusée explose au bout de 40s de vol. Le comble c'est que le bout de programme en question n'aurait jamais dû être utilisé à un autre moment que pendant le compte à rebours et de toute façon n'aurait pas dû être utilisé sur autre chose qu'une fusée Ariane 3! Un autre exeple dans le domaine est une sonde destinée à étudier Mars (Mars Climate Orbiter) qui a finie carbonisée dans l'atmosphère de Mars parce qu'une partie du programme fonctionnait en système métrique et qu'une autre en système impérial. Là encore personne ne s'est rendu de rien jusqu'à ce qu'on ai plus jamais eu aucun contact avec la sonde.
    Bref, ce genre d'erreur complètement stupide, non seulement arrivent mais peuvent même avoir de grosses conséquences économiques.

    Bref, de toute façon on ne vas pas faire un débat ici entre typage fort et typage faible, les spécialistes s'en charge suffisamment. Si les deux existent c'est sans doute pas par hasard. Et puis c'est pas en empêchant les conversions implicites que tout d'un coup un typage fort va apparaître miraculeusement en C++.

    Si j'ai posé la question ici c'est parce personnellement (avec mon niveau de compétence en C++, faible, l'utilisation que j'en fais, et l'habitude que j'ai prise avec ocaml que j'ai plus souvent l'occasion d'utiliser (et qui a de nombreux avantages en plus du typage fort )) ça m'intéresse beaucoup de pouvoir m'interdire les conversion implicites en C++, langage que j'utilise ponctuellement pour me simplifier la vie, et que partout sur les sites qui traitent de C++ on explique comment fonctionne les conversions implicites, comment rajouter des conversions implicites définies par l'utilisateur... mais j'ai pas trouvé de site expliquant comment faire le contraire.

    Évidemment il n'est pas question pour moi de demander à ce que le C++ empêche par défaut les conversion implicites. Le langage est comme il est avec ses qualités et ses défauts. S'il était aussi nul que ça, il n'y aurait (j'espère...) pas autant de monde qui l'utilise. Maintenant c'est vrai que je suis assez fervent défenseur de l'utilisation de tout ce qui peut permettre de détecter des risques d'erreurs de manière totalement automatique, quitte à obliger d'ajouter un peu de code pour dire au compilateur qu'on sait ce qu'on est en train de faire.

    Au passage j'avais bien réfléchi avant de poser ma question sur la façon dont j'allais la poser parce j'ai bien conscience que ce n'est pas une manière naturelle de faire en C++ et qu'au départ j'étais sur le point de me lancer dans de très longues explications pour justifier pourquoi je voulais le faire. Vu la taille de mon deuxième message et de celui là, je suis pas certain d'avoir complètement réussi à me restreindre .

  7. #7
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Ce n'est peut être pas la meilleure solution, mais au moins elle fonctionne :
    Tu peux t'amuser à ré-écrire tous les types de base avec leurs opérateurs, mais en ne permettant aucune conversion implicite (pas de "operator int()", et la majorité des constructeurs "explicit").
    Ca peut être long, mais tu peux considérablement réduire la taille du code en faisant usage des templates.

    Un des avantages que ça apporte (outre l'absence de conversion implicites), c'est que tu peux rajouter des fonctions membres : "myFloat.Random()", "myFloat.Pow(myFloat f)", ... ou des features que n'ont pas les types de base (gestion de l'infini pour les entiers par exemple).

    Un des inconvénients, c'est l'interface avec du code qui n'utilise pas ces types : il faut passer par des "myFloat.Get()".

    J'utilise ce genre de classes pour mon projet actuel, et j'en suis plutôt content pour le moment.
    Ça force à réfléchir à quel type on a besoin, parce qu'on va essayer de réduire le nombre de conversions au minimum. Au final, le code y gagne en clarté et en puissance : on peut du coup écrire sans se poser de question :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void MyFunction( myFloat fX, myFoat fY )
    {
        // Ces fonctions attendent une position sur l'écran.
        // On sais qu'on a ici des valeurs relatives par exemple
    }
     
    void MyFunction( myInt iX, myInt iY )
    {
        // ... et ici des valeurs absolues
    }
    Alors bien sûr, j'imagine que tout ça réduit les performances... Pour les traitement lourds, je préfère revenir aux types de base. Mais j'utilise toujours ces nouveaux types pour les arguments des fonctions et les valeurs de retour.

    Si tu es intéressé (et que tu t'en sens le courage), tu peux faire un tour sur le SVN de mon projet.
    Ce n'est peut être pas vraiment optimisé, mais l'ensemble fonctionne très bien : j'y ai redéfini tous les types dont je me sert souvent (int, uint, float, double, std::string) plus certains conteneurs de la STL (via l'héritage pour ceux là).

    Après, ce que tu met dans tes classes...
    J'ai choisi d'ajouter l'infini pour les entiers, tu n'en a peut être pas besoin (surtout que ça encombre pas mal les opérations...).
    J'ai pas mal joué avec la conversion vers le type string, pour pouvoir écrire ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    s_float f = 1.0f;
    s_int i = -5;
    s_str s = "Le float vaut : "+f+", et l'entier : "+i+".";
    (une version avec l'operateur de flux "<<" existe aussi).
    Ce n'est pas vraiment de la conversion implicite : "s_str:: operator +" est défini pour chaque type de base, il fonctionne différemment selon l'argument.

    Bref, ce que l'on met dedans dépend des gouts de chacun...
    Mais ça ouvre d'autres possibilités intéressantes.

  8. #8
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    Citation Envoyé par naadhicar Voir le message
    Même si j'aurais préféré un truc qui soit propre
    au langage et pas au compilateur, au cas où je serais amené à utiliser un
    autre compilateur.
    Tu n'auras rien. En passant, pour bien comprendre les unsigned il faut
    savoir qu'ils modèlent Z/n et non pas { x | 0 <= x < n }. (Et j'ai
    effectivement tendance à considérer que l'utilisation des unsigned pour le
    second ensemble est problématique parce que les règles du langages ne sont
    pas conçue pour. Cette utilisation est suffisemment commune pour que
    certains compilateurs avertissent dans certains cas où les problèmes
    peuvent apparaître).

    Pour répondre à JolyLoic, ce que je cherche est effectivement
    d'empêcher toute conversion implicite. L'intérêt je suis pas le seul à le
    voir et c'est tout à fait possible, c'est l'intérêt du "typage fort" et tu
    peux chercher sur le net pour avoir des information détaillées. Attention
    il n'y a pas qu'une seule notion de typage fort;
    Tu peux donner la tienne?

    Par exemple on pourrait se demander quel est le sens (sémantique) à
    donner à une conversion "unsigned int" vers "int" notamment quand la valeur
    de l'entier non signé ne fait pas partie de la plage de valeurs des entiers
    signés.
    En C++, la réponse est "implementation dependant". Les implémentations que
    je connais définissent la réponse en deux étapes:
    - passage au type unsigned de même taille que le type entier cible
    - choix de la valeur entière valide dans la classe d'équivalence

    L'inverse aussi me pose problème: que signifie une conversion de
    '-2' ou '2.6' vers un entier non signé?
    Il y a deux questions ici.

    -2 est parfaitement bien défini (les unsigned modelant Z/n)

    2.6 est aussi bien défini (la conversion tronque). (Là j'aurais préféré
    que le langage exige une conversion explicite).

    Idéalement j'aimerais aussi interdire "double d = 1;" mais j'ai
    l'impression que ça ne vas pas être possible en C++ vu qu'il n'a pas de
    notation séparée pour les entier de type double ou float.
    1.

    est un double.

    D'ailleurs C++ est-il capable de faire une différence entre le '1'
    entier signé et le '1' entier non signé par exemple.
    Il y a des notations de littéraux: 1 et 1U

    La surcharge permet de distinguer les cas. Maintenant j'aurais tendance à
    pas très bien accepter un programme qui ne fait pas la chose évidente dans
    les deux versions.

    Pour te donner un exemple d'un autre langage que j'utilise
    régulièrement voici des exemples en ocaml.
    Ocaml, c'est plutôt le balancier qui va trop loin dans l'autre sens (ne pas
    utiliser le même opérateur pour l'addition des entiers et des flottants,
    sincèrement...)

    Par exemple en C++ l'expression "a/2" n'aura pas le même sens
    suivant que a sera déclaré comme un float ou comme un int. Tant mieux si le
    programmeur s'assure systématiquement avant d'écrire une division (et plus
    généralement n'importe quel expression et appel de fonction) de bien
    connaître le type de "a" et qu'alors l'expression à bien le sens qu'il
    souhaite; et que plus tard (peut-être même beaucoup plus tard) personne ne
    vienne jamais changer la déclaration de a pour le faire passer d'un int à
    un float par exemple (une personne - dangereuse, à envoyer sur le bûcher
    immédiatement- qui ferait ça n'ira pas forcément regarder toutes les
    utilisations de "a" qui ont pu être faite et corriger correctement tout ce
    qui a besoin de l'être, imagine que ce ce soit une librairie).
    Même Ada permet la surcharge; et on pourrait argumenter qu'Ocaml est plus
    faiblement typé qu'Ada (quoi, pas de types contraints?). (Et je connais
    trop mal Ocaml pour en être sûr, mais j'ai le souvenir que si Ocaml ne la
    permet pas, c'est à cause de l'inférence de type qui, soit dans l'absolu,
    soit dans la version utilisée par Ocaml, ne permet pas de s'en sortir.)

    Note: j'ai pas le temps de tout lire et de répondre à tout d'une fois. Je compte passer sur les autres messages pour lesquels j'ai quelque chose à dire plus tard (pas ce soir, vraisemblablement pas demain, j'espère lundi).

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Interdire les touche ALT F4
    Par phoon dans le forum Access
    Réponses: 1
    Dernier message: 08/03/2006, 17h24
  2. [Struts]Interdire les cookies
    Par yush dans le forum Struts 1
    Réponses: 4
    Dernier message: 07/02/2006, 13h11
  3. Réponses: 2
    Dernier message: 22/07/2005, 08h52
  4. [jdbc][oracle] conversion implicite erronée
    Par Jack Huser dans le forum JDBC
    Réponses: 2
    Dernier message: 30/06/2005, 10h23

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo