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++

  1. #1
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    5
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 5
    Points : 1
    Points
    1
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    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
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    5
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 5
    Points : 1
    Points
    1
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    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
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    5
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 5
    Points : 1
    Points
    1
    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 éclairé

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

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    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
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    5
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    En fait c'est ce genre de choses auxquelles je faisait allusion dans ma question initiale par "par exemple en redéfinissant des classes reproduisant les types de base...". Si je posais la question d'interdire les conversions implicites c'était justement pour éviter d'avoir à faire ce genre de choses.

    J'ai essayer ce genre de solution et je trouve ça un peu lourd, d'autant que n'étant pas du tout spécialement à l'aise en C++ je suis jamais sûr de vraiment empêcher ce que je voudrais empêcher. Au début je suis parti dans cette direction en remarquant qu'on pouvait utiliser le mot clef "explicit" devant la déclaration du constructeur pour empêcher qu'il soit appelé implicitement. Seulement après il faut effectivement redéfinir tous les opérateurs.

    Pour définir mes propres types ça va (là par exemple tu ajoutes l'infini donc ça peut valoir le coup vu que de toute façon tu es bien obligé de modifier la définition des opérateurs pour l'utiliser) mais redéfinir les types de bases avec tous les opérateurs je trouve ça lourd.
    D'autant que tu dis qu'il faut ne pas redéfinir int() pour éviter les conversions implicites, sauf que si je comprend bien (mais je n'avais pas pensé à ça, je n'ai pas essayé donc je dis peut-être des bêtises) ça peut empêcher toute conversion, même explicite et que tu dois alors définir une autre fonction get pour faire la conversion. Je ne souhaites pas totalement interdire la conversion et si besoin je ne veux pas m'interdire d'utiliser explicitement les opérateurs de cast (vu mon niveau en C++ je risque d'avoir beaucoup de mal à reproduire les trucs du genre static_cast, dynamic_cast...), juste le fait qu'ils soient utilisés implicitement.
    Serait-il possible de redéfinir un opérateur de cast simplement en indiquant qu'il faut lui ajouter un mot clef du genre de "explicit" utilisé pour les constructeurs? Je n'ai rien trouvé là-dessus.

    En plus, toi même, alors que tu t'es déjà tapé tout le boulot de redéfinition, tu dis que tu continues à utiliser les types de base. Donc finalement on n'a pas réglé grand chose.

  9. #9
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par naadhicar Voir le message
    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.
    C'est bien comme ça que ça se passe. Dans le même style, et de mémoire, je crois que la norme laisse le choix entre -2. et -3. pour floor(-2.5). Et que ça risque d'être imposé comme étant -2 lors de la prochaine version de la norme, pour correspondre aux normes en vigueur en informatique, qui en l'occurrence me semblent moins pratiques que la définition mathématique de la partie entière.
    Citation Envoyé par naadhicar Voir le message
    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.
    Peut-être y a-t-il moins de types de base (je ne connais pas, j'ai juste lu 2/3 introductions) ? Mais en tout cas, en C++, il y a plein de code où des conversions parfois cachées, mais très souvent anodines, sont utilisées. Quelques exemples :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if (myVector.size() == 0) {...}
    if (mySmartPointer) {...}
    sqrt(0.1f);
    Citation Envoyé par naadhicar Voir le message
    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.
    Pas forcément. Convertir un int en long est sans pertes, par exemple. Idem float en double (et il y a souvent moins de pertes à convertir un float en double, faire un calcul, reconvertir en float, que de faire directement le calcul en float). Convertir un signed en unsigned est sans perte, au sens où on peut revenir à la valeur initiale, même s'il y a risque de changement de sens.
    Citation Envoyé par naadhicar Voir le message
    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.
    Je crois qu'un des buts de ce flag (je connais assez peu gcc) est justement de te signaler les cas où une perte est probable.
    Citation Envoyé par naadhicar Voir le message
    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.
    Surtout que là, il faudrait être non pas expert en C++, mais expert en gcc/c++... Et je ne crois pas que gcc puisse permettre de désactiver un warning pour un bloc de lignes particulier, une fois qu'on a validé que si, c'est bien ce comportement qu'on veut.
    Citation Envoyé par naadhicar Voir le message
    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.
    Souvent, du code à 10000 warnings, tu l'as en reprenant le code d'un autre, ou en portant sous un autre compilo (ou une autre version du même, ou d'autres options de ligne de commande...).
    Citation Envoyé par naadhicar Voir le message
    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.
    Je suis d'accord dans le cas général. C'est pour le cas "remplacer int par float" que j'ai du mal à y croire. Remplacer int par long, ou short, pourquoi pas, ou float par double. Mais int par float...
    Citation Envoyé par naadhicar Voir le message
    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++.
    Ce qui me gêne un peu, c'est que je considère que le C++ a déjà un typage relativement fort. Moins que d'autres langages, bien entendu, mais beaucoup plus que d'autres aussi.
    Citation Envoyé par naadhicar Voir le message
    Pour définir mes propres types ça va (là par exemple tu ajoutes l'infini donc ça peut valoir le coup vu que de toute façon tu es bien obligé de modifier la définition des opérateurs pour l'utiliser) mais redéfinir les types de bases avec tous les opérateurs je trouve ça lourd.
    Pour info, les types flottants de base gèrent déjà la notion d'infini sur quasiment toutes les implémentations du langage.
    Citation Envoyé par naadhicar Voir le message
    D'autant que tu dis qu'il faut ne pas redéfinir int() pour éviter les conversions implicites, sauf que si je comprend bien (mais je n'avais pas pensé à ça, je n'ai pas essayé donc je dis peut-être des bêtises) ça peut empêcher toute conversion, même explicite et que tu dois alors définir une autre fonction get pour faire la conversion. Je ne souhaites pas totalement interdire la conversion et si besoin je ne veux pas m'interdire d'utiliser explicitement les opérateurs de cast (vu mon niveau en C++ je risque d'avoir beaucoup de mal à reproduire les trucs du genre static_cast, dynamic_cast...), juste le fait qu'ils soient utilisés implicitement.
    Serait-il possible de redéfinir un opérateur de cast simplement en indiquant qu'il faut lui ajouter un mot clef du genre de "explicit" utilisé pour les constructeurs? Je n'ai rien trouvé là-dessus.
    Pas encore. C'est prévu pour la prochaine version du standard C++.
    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.

  10. #10
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Si l'on se restreint aux types entiers (comme int, unsigned int, char, long etc...), il existe une petite librairie extrêmement bien fichue qui permet de détecter les erreurs de cast implicite : SafeInt.

    Elle est d'ailleurs tellement pratique qu'elle a été intégrée directement dans la beta de Visual Studio 2010, dans le header <safeint.h>.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    // quelques typedef pour alléger l'utilisation
    typedef SafeInt<int> sint;
    typedef SafeInt<unsigned int> suint;
    typedef SafeInt<char> schar;
    typedef SafeInt<unsigned char> suchar;
     
    int a = -1;
    suint b = 1 + a + 2 - 7; // lance l'exception SafeIntArithmeticOverflow
    unsigned int c = (suint) 1 + a +2 -7; // lance l'exception SafeIntArithmeticOverflow
    suchar d = 254;
    char e = d //  lance l'exception SafeIntArithmeticOverflow
    Citation Envoyé par naadhicar
    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.
    Chez moi, avec visual studio, la compilation de ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    unsigned int a = -2; 
    int b = -2.5;
    émet :
    warning C4245: 'initializing' : conversion from 'int' to 'unsigned int', signed/unsigned mismatch
    warning C4244: 'initializing' : conversion from 'double' to 'int', possible loss of data
    Citation Envoyé par naadhicar
    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.
    Ben non justement, le compilateur n'émet un warning que si la conversion peut potentiellement faire perdre des information. Les conversions vers un type plus grand (comme passer d'un float à un double) sont sans danger.

    Edit : Par contre, les cast unsigned -> signed sont effectivement dangereux et je ne comprends pas trop pourquoi les compilateurs n'émettent pas de warning...

  11. #11
    Membre éclairé

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

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    Par défaut
    Citation Envoyé par naadhicar Voir le message
    En plus, toi même, alors que tu t'es déjà tapé tout le boulot de redéfinition, tu dis que tu continues à utiliser les types de base. Donc finalement on n'a pas réglé grand chose.
    ... pour les traitement lourds seulement
    Je n'en ai que très peu dans mon code.

  12. #12
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Chez moi, avec visual studio, la compilation de ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    unsigned int a = -2; 
    int b = -2.5;
    émet :
    warning C4245: 'initializing' : conversion from 'int' to 'unsigned int', signed/unsigned mismatch
    warning C4244: 'initializing' : conversion from 'double' to 'int', possible loss of data
    Pour information, g++ signale également ces problèmes :

    E:\projet\test_DVP\main.cpp||In function `int main()'
    E:\projet\test_DVP\main.cpp|7|warning: converting of negative value `-0x000000002' to `unsigned int'|
    E:\projet\test_DVP\main.cpp|8|warning: converting to `int' from `double'|
    ||=== Build finished: 0 errors, 2 warnings ===|

  13. #13
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    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).
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  14. #14
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par naadhicar Voir le message
    Peut-être tout simplement que le langage C++ n'est
    pas du tout adapté à ce que je veux faire.
    Ou à comment tu veux le faire...

    En passant, apprendre un nouveau langage, ce n'est pas uniquement
    comprendre comment il fonctionne (ce qu'à te lire je ne suis pas sûr que ce
    soit déjà bien le cas pour toi en ce qui concerne le C++), mais aussi
    comment l'utiliser. Chaque langage a ses caractéristiques, ses us et
    coutumes et vouloir aller contre eux c'est se battre à contre courant,
    parfois possible, jamais aisé et ne facilitant pas la communication --
    directe ou implicite à travers le programme, tu parlais de maintenance,
    l'utilisation de techniques inhabituelles est plus un problème en
    maintenance que certaines caractéristiques pas particulièrement heureuses
    du langage -- avec les autres programmeurs.

    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.
    Mais tu veux un typage fort? Je ne puis que te redemander ta définition de
    typage fort.

    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.
    Ce n'est même pas vrai en pour les pointeurs. (Un cast peut changer la
    représentation binaire d'un pointeur, en particulier en présence d'héritage
    multiple).

    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.
    Je ne vois pas pourquoi.

    Tout ceci ne concerne évidemment pas les experts en C++ qui
    connaissent tout ça sur le bout des doigts.
    L'utilisation de la troncature est partagée avec un certain nombre de
    langages (à commencer par Fortran). Sincèrement, c'est le genre de choses
    pour lesquelles un programmeur s'attend à devoir vérifier (parce qu'entre
    la troncature, l'arrondi au plus proche -- utilisé par Ada -- et la partie
    entière, on a trois possibilités intéressantes; les deux premières ayant
    l'avantage d'être des fonctions impaires).

    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
    Peut-être qu'il faut chercher. -Wall -W est le niveau minimum que
    j'utilise avec GCC.

    Attention, l'art des warnings est de ne pas en générer pour du code valide,
    correct et commun. Et l'utilisation de -1, -2 pour des non signés est
    vraisemblablement suffisemment courante (bien que -1U, -2U serait
    préférable) pour que je comprenne qu'un compilateur ne génère rien dans ce
    cas.

    (par exemple du genre l'histoire de débordement de conversion
    implicite dont je parlais plus haut),
    Je croyais que tu ne comprennais pas la non génération du warning dans
    certains cas. Ici le message "overflow in implicit constant conversion" me
    semble clair: il y a un dépassement de capacité pendant une conversion
    implicite d'une constante (note que je me méfie suffisemment de la
    traduction des messages d'erreur de gcc pour l'avoir désactivée depuis
    longtemps, elle est peut-être source de ton problème comme elle a peut
    avoir fait des progrès).

    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.
    Plus précisément pour éviter qu'un constructeur avec un paramètre soit
    considéré comme une conversion implicite définie par l'utilisateur.

    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.
    Faux. On a réutilisé un composant complet -- soft et hard -- en dehors de
    l'enveloppe pour laquelle il a été conçu sans prendre la peine de vérifier
    que les trajectoires d'Ariane V restaient bien dedans (c'est pas simple, de
    mémoire le débordement a eu lieu sur une valeur calculée et sans
    signification physique simple) en considérant que les tests devaient
    détecter les problèmes; puis on a annullé les tests d'intégration. Cette
    erreur de management en deux temps -- séparés par des années et commis par
    des personnes différentes -- aurait été indétectée si on n'avait pas choisi
    de conserver ce composant actif pendant qu'il n'était plus nécessaire.
    Mais là ce n'est pas tant une erreur qu'un compromis, désactiver le
    composant alors qu'il est inoffensif c'est introduire de la complexité
    supplémentaire qui peut aussi poser des problèmes; l'épisode d'Ariane V
    fait qu'on va pencher de nos jours pour l'autre côté du compromis, jusqu'à
    ce qu'une catastrophe arrive à cause de cette complexité...
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  15. #15
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    C'est bien comme ça que ça se passe. Dans le même
    style, et de mémoire, je crois que la norme laisse le choix entre -2. et
    -3. pour floor(-2.5).
    floor() est -3. Je crois que tu confonds avec la division d'entier (qui
    sera impaire sur ses deux arguments et operator% sera défini en fonction).
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  16. #16
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    floor() est -3. Je crois que tu confonds avec la division d'entier (qui
    sera impaire sur ses deux arguments et operator% sera défini en fonction).
    Oui, c'est bien ça. Merci pour la correction.
    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.

  17. #17
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    5
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Bon je ne vais pas répondre à tout parce que sinon je vais juste faire des copier/coller de trucs que j'ai déjà dits. Pour avoir mes réponses à un certaines nombres de remarques il suffit juste de relire de ce que j'ai déjà écrit.

    Sinon un truc sur lequel que j'ai pas précisé parce qu'au départ je voulais juste une info et pas entamer un débat sur l'opportunité de faire tel ou telle chose: je ne suis pas du tout en phase "d'apprentissage" du C++ mais plutôt en train de me demander si je vais l'utiliser ou pas, et donc s'il permet de faire des choses que moi j'estime nécessaires ou utiles, et ensuite peser le pour et le contre.
    À la base C++ m'intéresse uniquement parce que je voudrais utiliser Qt pour faire une interface graphique et que Qt est livré en standard uniquement avec une interface C++ et Qtcreator que j'ai l'intention d'utiliser pour ne pas me prendre la tête sur comment est-ce qu'on programme une IHM, et comme Qtcreator ne génère que du C++... Du coup je me pose la question de savoir si ça vaut le coup que je prenne du temps pour apprendre correctement le C++ pour pouvoir programmer les fonctionnalités du programme, ou est-ce que j'essaie de me débrouiller pour avoir un programme C++ de base qui fera la gestion de l'IHM et me débrouiller pour faire un binding vers un autre langage pour y programmer les fonctionnalités. Cela-dit les histoires de binding ne me tentent pas spécialement, même s'il y a SWIG qui permet de se simplifier la vie, avec une bibliothèque graphique dans le lot ça ne me rassure pas.
    Je n'étudie d'ailleurs pas seulement la possibilité d'utiliser C++ mais aussi d'autres langages comme python qui a une bibliothèque pyqt qui fait déjà le lien avec Qt mais sans m'y être vraiment intéressé pour le moment car ce langage ne me tente pas trop. Il semble y avoir d'autres projets par ci par là, je suis en train de regarder tout ça et essayer de faire un tri. Et pourquoi Qt? parce que j'aime bien le look et la gestion des thèmes graphiques intégrées à la bibliothèque. Et depuis que Qt a été porté sous Windows et MacOS ça m'intéresse encore plus. J'aime pas beaucoup gtk, si je suis sous KDE et pas sous Gnome c'est pas par hasard...

    Sinon c'est uniquement pour un projet personnel qui n'aura sans doute d'intérêt que pour moi et dont personne n'entendra probablement jamais parlé.
    D'un point de vue professionnel je ne "programme" pas à proprement parler. Je suis spécialisé en méthodes formelles... une fois les spécifications formelles bien ficelées et les algorithmes principaux prouvés je refile le bébé aux développeurs et aux équipes de test, quand je m'aperçois qu'un programme ne fonctionne pas parce qu'un programmeur a jugé qu'en simplifiant un peu des structures de données ça permettrait de faire un logiciel moins gourmand en ressources... ceci explique sans doute beaucoup de choses, déformation professionnelle oblige... Comme les clients de ma boîte sont surtout le transport, c'est pour ça que ce sont des exemples qui me viennent plus facilement à l'esprit. Quand on se casse le c.. pour s'assurer qu'un programme ne comporte plus (ou beaucoup moins) d'erreurs à condition de bien respecter la spécification d'environnement et qu'on voit que par la suite ils s'amusent à combiner des programmes n'importe comment...ça énerve. Surtout que pour moi le respect des structures de données (dont le respect des types est le fondement) est quand même une base de la bonne réutilisation de ce qui a été fait. Personnellement je ne n'imagine pas passer un pointeur sur une chaîne de caractère en argument d'une fonction qui a été développée pour prendre en argument un entier. Exactement de la même manière je n'imagine pas passer en argument un "int" à une fonction qui a été développée pour prendre un "unsigned int" en paramètre. Le problème des conversion implicites est justement pour moi qu'elles sont """"implicites"""" c'est à dire qu'il y a une fonctionnalité ajoutée par le compilateur, qui modifie la sémantique des données (un "int " ce n'est quand même pas la même chose qu'un "unsigned int") et sans que ce soit dit clairement puisque c'est """implicite""". Personnellement c'est le genre de chose qui me dérange vraiment énormément qu'une modification de sémantique importante (pour moi un cast n'est vraiment pas quelque chose de négligeable) puisse ne pas être le résultat d'une décision réfléchie du programmeur. Si le cast est vraiment ce qu'il veut faire alors qu'il le fasse, mais qu'il se pose au moins la question. Un expert en C++ aura j'imagine parfaitement conscience de quand il y a une conversion implicite et de quand il n'y en a pas. Dans mes exemples c'était en général des affectations de valeur à des variables, on peut encore réussir à comprendre ce qui se passe exactement mais par exemple si je fais une addition a+b+c+d+e+f et que tous les termes ont des types numériques différents, personnellement je ne sais plus du tout quels casts vont être faits et dans quel ordre. Et évidement en général ce ne sera pas une addition de valeurs numériques mais des trucs plus complexes.
    En plus comme je regarde depuis quelques jours un livre sur C++ (pas terrible d'ailleurs) que je viens de m'acheter pour regarder ça de plus près (un livre d'apprentissage pas une référence pour experts) et des tutoriels par ci par là sur le net; à chaque fois on nous explique des trucs d'une certaine façon, et puis en fait en approfondissant on se rend compte ce n'est pas ce qu'on avait compris. Par exemple j'apprends encore là que les "unsigned int" ne correspondent pas à { x | 0 <= x < n } mais à Z/n ! (j'ai beau retourné mon bouquin dans tous les sens ça n'est pas expliqué). Du coup j'ai l'impression que je saurais jamais la signification exacte de ce que je vais écrire puisqu'à chaque fois que je vais approfondir je vais me rendre compte que ce n'est toujours pas ce que j'ai compris. Ça n'est pas la faute du langage C++ lui-même si les bouquins et les tutoriels expliquent mal les choses mais ça ne simplifie pas la vie. Dans mon domaine il y a une phrase qui revient de temps en temps: "pour faire ça il faudrait donner une sémantique formelle à C", sous-entendu que le C (donc aussi le C++ qui est quand même proche et même à mon avis plus compliqué) est trop compliqué pour qu'on puisse lui donner une sémantique formelle. Dans mon métier j'ai l'habitude de travailler avec des langages (les langages de Coq, B, ou PVS...) qui ont une sémantique formelle, donc pour connaître la signification exacte de quelque chose il suffit d'aller lire la sémantique (qui n'est pas forcément simple mais c'est toujours ça).

    Pour moi ça ne pose aucun problème qu'il y ait une notation différente par exemple pour l'addition sur les entiers et l'addition sur des flottants. Après tout ce sont des opérateurs différents avec un signification différente (additionner deux entiers ça n'est quand même pas la même chose que d'additionner deux flottants! même si ça se réfère à une même fonction mathématique, on fait de l'info pas des maths) donc pourquoi par leur donner un nom différent. Ça me dérange pas forcément que le C++ utilise de la surcharge pour utiliser un même nom lorsqu'on peut bien faire la différence, mais avec les conversions implicites on ne maîtrise pas forcément bien laquelle des deux va être utilisé. On peut évidemment se dire qu'il suffit de faire attention à chaque fois qu'on écrit une addition de regarder si les deux types sont les mêmes, puis si ce n'est pas le cas comment vont se faire les casts... C'est justement ça que je voudrais obtenir en m'interdisant les conversions implicites: je ne pourrai pas oublier de me poser la question du sens que je veux donner à une addition entre un entier et un flottant et expliciter les casts c'est me rendre responsable du résultat obtenu (si je me suis planté, le résultat ne sera pas bon mais ce sera de ma faute et pas parce qu'un compilateur en suivant ses propres règles que je ne maîtrise pas aura généré un cast plutôt qu'un autre).

    Déformation oblige, je ne fais pas confiance au programmeur pour penser à ce à quoi il doit penser, surtout si c'est moi le programmeur.

    Du coup je pense que tout ça répond à la question de l'intérêt que je trouve au typage fort. Sinon je vais pas m'amuser à donner une définition précise puisque ça n'existe pas, chaque langage ayant sa propre notion de typage et son degré de contrainte associé. Disons grosso modo que ce que j'attends c'est de ne pas pouvoir utiliser une expression d'un certain type lorsque c'est un autre type qui est attendu parce que pour moi une valeur dans un certain type a une certaine sémantique et que c'est à moi de décider à quelle autre valeur dans un autre type je veux lui faire correspondre (éventuellement via un cast standard si c'est là ce que je veux faire). Pour moi, mais je suis peut-être dans la déformation professionnelle à fond, le typage fort fait parti des techniques de base pour s'éviter de problèmes. Comme je l'ai déjà dit on peut toujours faire des incantations en espérant qu'un programmeur réfléchira bien à tout ce qu'il écrit pour s'assurer que ce qu'il écrit fait bien ce qu'il veut faire. Dans un monde idéal le typage fort ne servirait alors à pas grand chose. Mais la très grande majorité des bugs viennent des erreurs des programmeurs. Dans ma spécialité on a des techniques formelles pour s'assurer de ne pas faire de bêtise mais ça n'est quasiment jamais automatique et il faut se taper plein de preuves mathématiques pour s'assurer que ce qu'on a écrit est correct (et comme nous aussi on fait pleins d'erreurs, ces techniques nous permettent surtout de repérer et corriger nos erreurs). Comme on va pas demander à des programmeurs de se taper des preuves à longueur de journée (note que ça ne rallonge pas nécessairement le temps de développement car ça permet de réduire considérablement la phase de test, et puis ça coûte pas forcément plus cher car plus une erreur est repérée tôt moins elle coûtent cher, les méthodes formelles intervenant généralement en amont du développement; en plus un bon développeur est généralement mieux payé que le mec qui développe les spécifications formelles ) il faut des techniques entièrement automatiques capable de repérer certains problèmes. Le typage fort fait pour moi parti de ces techniques entièrement automatique permettant de s'éviter quelques problèmes. Évidemment ça ne règle pas tout, si tu te plantes en mettant une conversion explicite tu auras autant de problèmes, mais tu auras au moins eu un peu plus de chances de te poser la question. Comme moi quand je fais des spécifications formelles, si je me plante dans mes spécifications initiales ou dans l'écriture de mes contraintes formelles je vais aussi obtenir n'importe quoi mais comme il système de vérification va m'obliger à me poser la question de la cohérence entre les spécifications, les contraintes et les modèles formels je vais avoir plus de chance de repérer une erreur que si j'avais écrit directement le modèle (équivalent au programme) sans écrire de spécifications ni de contraintes. Et puis plus je vais rendre précise mes spécifications et ajouter des contraintes, plus j'augmente les chances de tomber sur des incohérences. Je considère le typage comme une forme simple de contrainte, qui a l'avantage de pouvoir être analysée complètement automatiquement. Du coup je vois une conversion implicite comme une modification, décidée par le compilateur et pas par moi, de la sémantique de mon programme de façon à forcer la cohérence entre le programme et les contraintes exprimées par le typage. Moi ça me dérange parce c'est à moi de décider en cas d'incohérence si c'est la contrainte exprimée par le typage qui n'est pas bon ou si c'est l'instruction que j'ai écris dans mon programme qui n'est pas correcte par rapport à la contrainte. Dans certains cas c'est peut-être les deux que serai amené à modifier. C'est pourquoi je disais que je trouve que le code devient rarement verbeux en me forçant à expliciter les conversions, parce que quand je pense mes programmes il doit y avoir une cohérence entre ce que j'exprime dans mon typage et ce que j'exprime dans les opérations ou les appels de fonctions. Sauf dans certains cas où la fonctionnalité que je souhaite correspond effectivement à "extraire" une valeur d'un certain type à partir d'une valeur d'un autre type (typiquement, toujours pour rester sur les exemples numériques, une fonction qui me donnerai le numérateur d'un nombre rationnel, ou la partie entière d'un nombre), les conversions devraient être rares, et devrait me faire poser la question du sens que je donne à tout ça. Par exemple si j'ai des couples de nombres d'entier qui représente les coordonnées d'un pixel sur l'écran (j'imagine qu'en pratique on ne représente pas la position des pixels par des couples d'entier mais peu importe c'est juste pour l'exemple), si je calcul une moyenne mathématiquement entre deux points pour obtenir le milieu, en général je ne tomberai pas sur des nombres entiers. Dans ce cas il me semble important de me poser la question de savoir de quelle façon je vais arrondir ces calculs car le résultat à l'affichage ne sera pas le même suivant ce que j'aurais choisi (peut être que dans certains cas je voudrais arrondir au dessus, dans d'autre arrondi en dessous, ou alors toujours du même côté, ou que l'arrondi dépendra des valeurs moyennes des deux paramètres ou encore de facteurs extérieurs... je sais pas, ça dépendra du contexte et de l'utilisation mais il me semble important d'avoir à me poser la question parce que le résultat ne sera pas le même au final (c'est sûr que pour un pixel parmi je ne sais combien d'autres ça vas pas forcément être flagrant, c'est juste pour le principe).


    J'ai bien compris que c'était pas vraiment la philosophie naturelle de C++. En fait j'ai appris à programmer en ADA à la base, ça explique peut-être aussi des choses. Et comble de l'horreur, j'ai appris les concepts de la POO en programmant en Smalltalk76...en 1997 (il y a des profs d'informatique qui n'ont peur de rien).

    Bref, quand je programme c'est uniquement dans des buts purement personnels (je crois pas que mon patron soit assez fou pour me mettre un compilateur C++ dans les mains) et j'ai pris l'habitude d'utiliser les langages... comment dire... Quand j'ai une idée que je veux tester rapidement sans critère d'efficacité et qui n'a pas besoin de structures de données complexes et dont je me fou de savoir si je fais des erreurs ou pas... je fais ça en Prolog. Par contre si je veux des structures complexes (la quasi totalité du temps avec moi ce sont des structures inductives) et des programmes efficaces parce que traitement lourd oblige... j'utilise ocaml.

    Pour ce qui concerne le coup des pointeurs j'avais l'impression qu'il n'y avait aucune information de type associé (donc aucune contrainte d'utilisation), que s'était juste des adresse mémoires, du coup je voyais pas bien l'intérêt d'y associer un type et encore moins de le contraindre. Naturellement j'aurais pas eu l'idée d'utilisé un pointeur directement dans une expressions, mais d'abord d'aller chercher la valeur (ou une partie) contenue à l'adresse mémoire et de l'attribuer à une variable qui elle a un type (une référence pour éviter d'avoir à créer concrètement une variable, mais c'est manifestement pas comme ça que ça se passe). Du coup l'interprétation du contenu binaire de la mémoire aurait dépendu du type de la variable à laquelle on l'aurait affecté. Bref, je ne voyais pas du tout de notion de typage (ni fort ni faible ni rien) dans un pointeur, une adresse c'est une adresse. Mais apparemment ce n'est pas du tout comme ça que ça marche, donc me renseignerai plus avant si je dois programmer en C++.

    Sinon plus concrètement je confirme que chez moi le programme suivant ne produit aucun commentaire de g++ (en ne mettant aucun flag). Je tape la commande "g++ code.cpp" et la ligne suivante est le nouveau prompt; aucun commentaire du tout, rien de rien.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int main(){
     
    unsigned int a = -2; 
    int b = -2.5;
    return 0;
    }
    C'est peut-être une question de version puisque je n'ai pas la dernière version en date (j'utilise la version 4.3.2) mais en tout cas je n'ai vraiment aucun commentaire.

    En ce qui concerne le "dépassement de capacité pendant une conversion
    implicite d'une constante" c'est plus ou moins ce que j'avais compris sauf justement que ce que je ne comprend pourquoi c'est pourquoi ça arrive! Si j'écris "unsigned int ui=-2.5;" je sais bien qu'un float négatif n'a aucune chance de rentrer telquel sauf qu'avec la conversion implicite que j'imaginais que le compilateur ajoute je pensais qu'il se chargerai de transformer, selon ses propres règles que je ne connaît pas vraiment, le tout en quelque chose qui rentrerait dans "unsigned int". Par contre si j'écris "unsigned int ui=(unsigned int)-2.5;" ou "unsigned int ui=static_cast<unsigned int>(-2.5);" là il n'y a plus de problème et si j'affiche la valeur j'obtiens "0". Ce que je ne comprend pas c'est pourquoi dans ce cas il n'a pas ajouté la conversion implicite alors que si je fait "int i=2.5;" il ajoute automatiquement la conversion implicite. Et si je fais "float sf= 2.5; unsigned int ui=sf;" il n'y a plus de problème non plus mais par contre la valeur affichée est "4294967294"! C'est ça que je disais que c'était un comportement bizarre, des fois il ajoute des conversions implicites des fois non. Et quand j'ai l'impression d'écrire la même chose de deux façons différentes, ça ne donne pas le même résultat. Donc je me dis si j'arrive déjà pas à comprendre ça, comment je vais pouvoir d'une façon plus générale maîtriser les programmes que j'écris. Ça fait parti des choses que j'ai essayées pour voir si je comprend ce qu'on m'explique par ci par là et qui me font dire, comme je l'écrivais plus haut, qu'à chaque fois que je gratte un peu plus je me rend compte que j'ai en fait rien compris et que la façon dont on m'avait expliqué les choses simplement s'avère un peu n'importe quoi. Comme j'ai pas l'intention de devenir un expert en C++, ça fait partie des questions que je me pose quand à savoir si je vais choisir ce langage ou non pour être capable de faire des choses sans faire n'importe quoi, j'ai l'impression je les choses qui devraient être les plus simples sont déjà hyper-complexes. Quel est la différence entre "unsigned int ui=(unsigned int)-2.5;" et "float sf= 2.5; unsigned int ui=sf;" ? À part le fait d'utiliser une variable supplémentaire intermédiaire? Avec une idée tête j'ai essayé "unsigned int ui=(unsigned int)-2.0;" et "float sf= 2.0; unsigned int ui=sf;", ce qui a donné le même résultat et n'a pas confirmé mon idée, par contre j'ai ensuite essayé "unsigned int ui=(unsigned int)-2;" et "float sf= 2; unsigned int ui=sf;" et la valeur affichée est la même dans les deux cas: "4294967294". Donc la conversion du 2 entier et du 2 float vers un unsigned int ne donne pas toujours le même résultat selon que l'on passe par une variable intermédiaire ou non. C'est trop compliqué pour que j'espère de pas oublier de penser systématiquement à ce genre de choses.

    Sinon si tu sais comment désactiver la traduction en français des messages de GCC ça m'intéresse, j'ai juste installer le métapackage C++ de ma distribution et s'était directement en français.

    Pour Ariane 5 tu n'as fait que redire exactement ce que j'avais dit: on a réutilisé un truc qui n'était pas fait pour l'environnement dans lequel il a été mis. C'était juste une illustration très célèbre pour dire qu'on ne peut pas espérer que des gens ne fassent n'importent quoi. Tout ingénieur sait bien qu'on ne s'amuse à prendre un composant et à l'intégrer dans un autre environnement sans s'assurer (par des tests ou d'autres techniques) qu'il va se comporter correctement dans cet environnement (c'est d'ailleurs une question qui dépasse l'informatique et qui est applicable dans beaucoup de domaines); j'espère que, vu le prix que ça coûte, on ne s'amuse pas à mettre tout et n'importe quoi sans réfléchir à l'intérieur d'Arine5 en se disant "qu'on va bien voir"; et puis j'imagine que les ingénieur du projet Ariane5 ne sont pas des derniers de la classe; et pourtant ils l'ont fait la bourde! Dons restons sur terre, et espérer que personne n'ira changer un int en float pour moi ce ne sont que des incantations, très louables, mais qui n'empêcheront rien. Je pense que les développeur C++ pro doivent en voir des vertes et pas mûres que je ne suis même pas capable d'imaginer. (Le coup du mec qui fait une liste chaînée avec des pointeurs pour représenter une liste de bits... et estimons-nous heureux qu'il ne soit pas allé nous faire une liste doublement chaînée; il paraît que s'est un classique des entretiens d'embauche de développeur).


    Bref, je n'avais pas l'intention d'entamer une discussion sur le sujet mais juste savoir si c'était possible de s'empêcher de faire des conversions implicites. J'ai eu ma réponse: c'est pas possible dans C++ mais g++ à un flag qui permet d'en signaler une partie. C'est pourquoi j'ai mis depuis longtemps [Résolu] dans le titre et que je viens pas voir si quelqu'un d'autre à ajouter un commentaire et je sais pas si je vais revenir pour re-répondre à nouveaux commentaires. Je suis pas du tout à l'aise en C++ pour pouvoir avoir un débat sur l'opportunité de faire tel ou tel chose en C++. En plus j'ai facilement tendance à faire toujours long dans mes réponses, je trouve déjà ça pénible moi même, alors pour ceux qui se donnent la peine de tout lire... Pour le moment j'essaie juste de voir ce qui est possible ou non, l'intérêt que ça peut avoir pour mon utilisation personnelle et au final je verrai bien la décision que je prendrais avec les informations que j'aurai aussi recueillies sur les autres langages. Après seulement, le cas échéant, je m'intéresserai de plus près à la signification exactes des choses et des parties du langage que j'utiliserai ou non.

  18. #18
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par naadhicar Voir le message
    Bon je ne vais pas répondre à tout
    Moi non plus (pour d'autres raisons) et je reviendrai sur la partie plus C++ que générale.

    Je n'étudie d'ailleurs pas seulement la possibilité d'utiliser C++ mais aussi d'autres langages comme python qui a une bibliothèque pyqt qui fait déjà le lien avec Qt mais sans m'y être vraiment intéressé pour le moment car ce langage ne me tente pas trop.
    D'après ce que tu écrits, Python n'est pas ce que tu cherches. Beaucoup trop éloigné de ce que tu cherches vu l'importance que tu accordes au typage statique. Je me demande si tu ne ferais pas mieux de laisser tomber QT et de chercher quels interfaces graphiques sont disponibles en Ocaml.

    Sinon plus concrètement je confirme que chez moi le programme
    suivant ne produit aucun commentaire de g++ (en ne mettant aucun flag). Je
    tape la commande "g++ code.cpp" et la ligne suivante est le nouveau prompt;
    aucun commentaire du tout, rien de rien.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int main(){
     
    unsigned int a = -2; 
    int b = -2.5;
    return 0;
    }
    C'est peut-être une question de version puisque je n'ai pas la dernière
    version en date (j'utilise la version 4.3.2) mais en tout cas je n'ai
    vraiment aucun commentaire.
    Pour avoir un niveau minimum de warning avec gcc, il faut les demander.
    -Wall -Wextra (ou -Wall -W pour les versions plus anciennes) me semble un
    minimum.

    Sinon si tu sais comment désactiver la traduction en français des
    messages de GCC ça m'intéresse, j'ai juste installer le métapackage C++ de
    ma distribution et s'était directement en français.
    env LC_MESSAGES=en_US g++ ...

    Pour Ariane 5 tu n'as fait que redire exactement ce que j'avais
    dit
    [...]
    et puis j'imagine que les ingénieur du projet Ariane5 ne sont pas des
    derniers de la classe; et pourtant ils l'ont fait la bourde!
    L'important de ma remarque t'a échappé. C'était un ensemble de choix
    conscients qui ont mené au problème et chacun de ces choix était
    sensé quand on ne considère que ce que savaient les gens qui les ont
    faits. C'est un problème de gestion de projet (comment arriver à connaître
    tous les facteurs significatifs pour prendre une décision), pas un problème
    technique (même si on peut après coup retracer les détails techniques --
    j'en ai corrigé un dans ce que tu disais -- mais ce ne sont que des détails
    sans grande importance sur le fond).
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  19. #19
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par naadhicar Voir le message
    En ce qui concerne le "dépassement de capacité
    pendant une conversion implicite d'une constante"
    La conversion d'un flottant vers un entier tronque et si le résultat n'est
    pas représentable, il est indéfini. Autrement dit n'importe quoi
    peut arriver. Et quand les optimiseurs se mettent à profiter des
    comportements indéfinis, tu peux réellement avoir n'importe quoi, même des
    effets apparemment antérieur à l'exécution du code ayant le comportement
    indéfini. Les comparatifs de perfs du code généré poussent les
    compilateurs dans cette voie, même si ce n'est pas toujours du goût de ceux
    qui pensent que ces comportements devraient être le comportement "naturel"
    sur la machine cible (il y a parfois un gros problème de définir quel est
    ce comportement naturel et pour un compilateur comme gcc qui vise des
    architectures différentes, mettre en place une organisation qui permettent
    d'atteindre le comportement naturel même pour des marchines qui n'existent
    pas encore peut être un gageure).

    Les comportements indéfinis, c'est la plaie du programmeur. En
    particuliers des programmeurs qui ne connaissent pas bien le langage et qui
    se fient à des essais (pour eux, le comportement "naturel" fini par être le
    comportement du premier compilateur sur lequel ils ont testé quelque chose
    de plus ou moins similaire. La plupart des comportements indéfinis (mais
    pas tous) sont hérités du C et pour la plupart, s'ils sont indéfinis c'est
    que le cas général n'est pas facilement statiquement détectable, qu'on a
    considéré que des cas particuliers ne méritaient pas la complexité qu'ils
    introduisaient et que les compilateurs faisaient déjà plus ou moins
    n'importe quoi alors (le mandat des comités de normalisation du C et du C++
    était de normaliser une pratique existante). Dans le cas présent, le cas
    général doit être des choses comme 1E30 pour lequel il doit y avoir des
    machines générant des interruptions si on ne les traite pas
    particulièrement. Et le cas particulier qui n'a pas été jugé digne d'être
    traité est celui des unsigned (pour lesquels à cause du comportement
    modulaire, on pourrait définir des valeurs sensées).

    Bon, g++ t'averti (pas une erreur, c'est du code correct... tant qu'il
    n'est pas exécuté) quand il détecte un comportement indéfini (mais le
    warning pourrait être encore plus clair en disant explicitement que c'est
    un comportement indéfini), mais il considère probablement que quand tu mets
    un cast, tu lui dit de fermer sa grande gueule et que tu sais ce que tu
    fais et il ne fait pas une analyse assez poussée pour voir qu'il y a un
    problème quand tu passes par la variable intermédiaire.

    Et si je fais "float sf= 2.5; unsigned int ui=sf;" il n'y a plus
    de problème non plus mais par contre la valeur affichée est
    "4294967294"!
    C'est ce le comportement que voudraient vraisemblablement les partisants du
    comportement naturel. C'est la valeur de
    static_cast<unsigned>(static_cast<int>(-2.6)) (rapelle toi le comportement
    modulaire des unsigned, sur ta machine le N est à 2^32, soit 4294967296,
    donc -2 donne 4294967294).
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  20. #20
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2009
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2009
    Messages : 6
    Points : 15
    Points
    15
    Par défaut
    pour ce cas je me sers d'un narrowing conversion.
    donc si je ne veux pas de conversion je mets la valeur entre accolades:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    unsigned int =   -5.3  ; // ok : conversion
    unsigned int = {-5.3}; // ko : narrowing conversion

+ 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