OVH Cloud OVH Cloud

Type bool en C++

13 réponses
Avatar
Pierre Maurette
Bonjour,
J'aimerais avoir des avis sur l'utilité du type bool en C++.
J'ai fait des tests sur quelques compilateurs (tous sous Windows),
configurés au plus près de la norme ANSI/ISO. Les résultats, bien que
conformes à la norme, sont troublants.

<code>
bool A = true;
bool B = true;
bool C;

C = (1>0) + (2>3) + (17>0) + (100>10);
printf("(printf)C -> : %u\n", C);
cout << " (cout)C -> : " << C << endl << endl;

C = A + B + A + B;
printf("(printf)C -> : %u\n", C);
cout << " (cout)C -> : " << C << endl << endl;

C = true;
C += C;

printf("(printf)C -> : %u\n", C);
cout << " (cout)C -> : " << C << endl;

if(C==true)cout << "C est vrai !" << endl << endl;
else cout << "C est faux !" << endl << endl;

C++;
printf("(printf)C -> : %u\n", C);
cout << " (cout)C -> : " << C << endl << endl;
</code>
(il aurait été plus correct de remplacer les printf() par des cout << (int)C
..., pour le même résultat)

Les résultats en GCC et VC6 sont conformes à "ma" logique :
<résultats>
(printf)C -> : 1
(cout)C -> : 1

(printf)C -> : 1
(cout)C -> : 1

(printf)C -> : 1
(cout)C -> : 1
C est vrai !

(printf)C -> : 1
(cout)C -> : 1
</résultats>

Avec Borland (5.5 et C++Builder 6 en compilation ANSI, ce qui n'est pas
simple !).
<résultats>
(printf)C -> : 1
(cout)C -> : 1

(printf)C -> : 1
(cout)C -> : 1

(printf)C -> : 2
(cout)C -> : 1
C est faux !

(printf)C -> : 3
(cout)C -> : 1
</résultats>

Si c'est pour ce genre de comportement, je ne vois pas l'intérêt d'un type
bool. A la limite, je préfère comme en C rester sur des int, et
éventuellement faire un :
typedef enum {false = (1 == 0), true = !false} bool;
ou
typedef enum {faux = (1 == 0), vrai = !faux} booleen;
en sachant au moins à quoi m'attendre.
Je ne comprends pas que les opérations d'arithmétique entière soient
acceptées (sans warning semble-t-il) sur de booléens. La raison pourrait
être que les comparaisons renvoient un int en C. Mais :
if((10<a)+(12>b)){...}
n'est certainement pas excellent et pourrait facilement être corrigé.
On peut sans problème comparer à false (if(C!=false) est correct), mais
jamais à true, ni à un autre bool.
L'égalité de deux bool (un OU exclusif, en fait) doit s'écrire (par
exemple):
if((A&&B)||(!A&&!B)){}
Donc, mes questions :
- Comment justifier la timidité de la norme ?
- Comment justifier le laxisme de Borland ?
Merci
Pierre

10 réponses

1 2
Avatar
kanze
"Pierre Maurette" wrote in message
news:<3f8bb008$0$20153$...

J'aimerais avoir des avis sur l'utilité du type bool en C++. J'ai fait
des tests sur quelques compilateurs (tous sous Windows), configurés au
plus près de la norme ANSI/ISO. Les résultats, bien que conformes à la
norme, sont troublants.

<code>
bool A = true;
bool B = true;
bool C;

C = (1>0) + (2>3) + (17>0) + (100>10);
printf("(printf)C -> : %un", C);
cout << " (cout)C -> : " << C << endl << endl;

C = A + B + A + B;
printf("(printf)C -> : %un", C);
cout << " (cout)C -> : " << C << endl << endl;

C = true;
C += C;

printf("(printf)C -> : %un", C);
cout << " (cout)C -> : " << C << endl;

if(C==true)cout << "C est vrai !" << endl << endl;
else cout << "C est faux !" << endl << endl;

C++;
printf("(printf)C -> : %un", C);
cout << " (cout)C -> : " << C << endl << endl;
</code>

(il aurait été plus correct de remplacer les printf() par des cout <<
(int)C ..., pour le même résultat)

Les résultats en GCC et VC6 sont conformes à "ma" logique :
<résultats>
(printf)C -> : 1
(cout)C -> : 1

(printf)C -> : 1
(cout)C -> : 1

(printf)C -> : 1
(cout)C -> : 1
C est vrai !

(printf)C -> : 1
(cout)C -> : 1
</résultats>

Avec Borland (5.5 et C++Builder 6 en compilation ANSI, ce qui n'est pas
simple !).
<résultats>
(printf)C -> : 1
(cout)C -> : 1

(printf)C -> : 1
(cout)C -> : 1

(printf)C -> : 2
(cout)C -> : 1
C est faux !

(printf)C -> : 3
(cout)C -> : 1
</résultats>

Si c'est pour ce genre de comportement, je ne vois pas l'intérêt d'un
type bool. A la limite, je préfère comme en C rester sur des int, et
éventuellement faire un :
typedef enum {false = (1 == 0), true = !false} bool;
ou
typedef enum {faux = (1 == 0), vrai = !faux} booleen;
en sachant au moins à quoi m'attendre.

Je ne comprends pas que les opérations d'arithmétique entière soient
acceptées (sans warning semble-t-il) sur de booléens.


Le but de bool, c'était de remplacer de nombreux typedef et al. en
existance. Sans casser le code qui les utilisait. Du coup, une
conversion implicite en int était nécessaire, ainsi (et surtout) leurs
compléments.

Les auteurs de la proposition voulaient déclarer ces conversions
« deprecated ». Le comité en a décidé autrement.

Un bon compilateur émettrait un avertissement. Malheureusement, des bons
compilateurs C++ sont rares.

La raison pourrait être que les comparaisons renvoient un int en C.


C'était en fait pour ça. D'une part, on voulait que le type d'une
comparaison soit bool (ne serait-ce que pour le surcharge). Mais
d'autre, il n'était pas question de casser des choses existantes comme :

int flag = a > b ;

Et ce n'était pas seulement une question de compatibilité C -- à
l'époque où ils ont ajouté bool, il y avait déjà pas mal du C++
existant.

Mais :

if((10<a)+(12>b)){...}

n'est certainement pas excellent et pourrait facilement être corrigé.


Il y a malheureusement une longue tradition, depuis C, d'écrire des
choses comme :

if ( p ) ...

Et évidemment, dans l'existant, il y avait des choses du genre :

if ( flag ) ...

où flag était int.

Comme j'ai dit, les auteurs de la proposition voulaient que ces
conversions soient « deprecated », mais le comité en voulait autrement.

On peut sans problème comparer à false (if(C!úlse) est correct),
mais jamais à true, ni à un autre bool.


On ne compare jamais un bool à une constante. En revanche, la
comparaison de deux bools est bien définis par la norme, et ce que tu as
vu avec Borland est une erreur.

L'égalité de deux bool (un OU exclusif, en fait) doit s'écrire (par
exemple):

if((A&&B)||(!A&&!B)){}
Donc, mes questions :
- Comment justifier la timidité de la norme ?


Compatibilité avec l'existant, et une vieille tradition de laxisme en ce
qui concerne les types dans les conditionnels.

- Comment justifier le laxisme de Borland ?


Ce n'est pas du laxisme, c'est une erreur, pûr et simple.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16

Avatar
Pierre Maurette
a écrit
[plein de trucs bien]

Merci pour vos réponses, qui me rassurent.
Un peu déçu quand même par les compilos Borland.
En fait, je cherchais à définir une stratégie cohérente, en C et en C++, et
ce sur du code neuf.
J'en arrive à :

- Différencier systématiquement les booléens des entiers, bool en C++,
typedef quelconque en C, et en tous cas dans l'esprit. Donc :
pas bon :
if(compteur)
if(condition == 0)
bon :
while(!fini && (compteur != 0)){}

- Refuser toute opération arithmétique sur des booléens. Ceci limite donc
les opérateurs à &&, ||, !, résultats de comparaisons et affectations par
d'autres booléens respectant ces contraintes (ou expressions contenant des
.. ). En C++, il faut espérer que le compilateur avertirait pour un truc
aussi douteux que :
bool condition1;
...
condition1++;
En C, il faudra être discipliné.

- Pour des fonctions renvoyant un booléen dont on ne serait pas certain de
la valeur entière du true (plutôt en C), on doit pouvoir "normaliser" par un
truc genre !!fonction().

- En C :
typedef enum (false, true) bool;
typedef enum (false, true = !false) bool;

- Dans ces conditions, peu contraignantes, il me semble qu'on aura toujours
des booléens comparables. C'est quand même pratique (et lisible) de pouvoir
écrire :
if(state_gate1 != state_gate2){}

C'est cohérent ?
Pierre
Avatar
Marc Boyer
wrote:
"Pierre Maurette" wrote in message
news:<3f8bb008$0$20153$...
On peut sans problème comparer à false (if(C!úlse) est correct),
mais jamais à true, ni à un autre bool.


On ne compare jamais un bool à une constante. En revanche, la
comparaison de deux bools est bien définis par la norme, et ce que tu as
vu avec Borland est une erreur.


Peux-tu préciser, j'ai du mal avec "constante".
Tu veux dire qu'on ne doit pas faire C != false ?

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(


Avatar
kanze
"Pierre Maurette" wrote in message
news:<3f8c6ed3$0$20642$...
a écrit

Merci pour vos réponses, qui me rassurent. Un peu déçu quand même par
les compilos Borland. En fait, je cherchais à définir une stratégie
cohérente, en C et en C++, et ce sur du code neuf.
J'en arrive à :

- Différencier systématiquement les booléens des entiers, bool en C++,
typedef quelconque en C, et en tous cas dans l'esprit. Donc : pas bon :

if(compteur)
if(condition == 0)
bon :
while(!fini && (compteur != 0)){}


C'était une bonne idée déjà avant bool.

- Refuser toute opération arithmétique sur des booléens. Ceci limite
donc les opérateurs à &&, ||, !, résultats de comparaisons et
affectations par d'autres booléens respectant ces contraintes (ou
expressions contenant des .. ). En C++, il faut espérer que le
compilateur avertirait pour un truc aussi douteux que :
bool condition1;
...
condition1++;


Ça serait bien. Ça serait même bien s'il avertissait pour tout
conversion entre bool et un type entier.

En C, il faudra être discipliné.


En C++ aussi, malheureusement.

- Pour des fonctions renvoyant un booléen dont on ne serait pas
certain de la valeur entière du true (plutôt en C), on doit pouvoir
"normaliser" par un truc genre !!fonction().


J'aurais tendance à dire plutôt « fonction() != 0 ».

Mais si c'est du code neuf, je me servirais des types bool (en C++) et
_Bool ou bool (avec inclusion de <stdbool.h>, en C) -- si mon
compilateur C n'implémente pas encore _Bool, je ferais un typedef et
deux #define. Et je ferais vérifier dans les revues de code que ce qui
était déclaré bool l'était réelement.

- En C :
typedef enum (false, true) bool;
typedef enum (false, true = !false) bool;


En C, #include <stdbool.h>. Avec plutôt :
typedef unsigned char bool ;
#define false 0
#define true 1
pour les anciens compilateurs C.

- Dans ces conditions, peu contraignantes, il me semble qu'on aura
toujours des booléens comparables. C'est quand même pratique (et
lisible) de pouvoir écrire :

if(state_gate1 != state_gate2){}

C'est cohérent ?


Tout à fait.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16

Avatar
Pierre Maurette
a écrit
[...]
certain de la valeur entière du true (plutôt en C), on doit pouvoir
"normaliser" par un truc genre !!fonction().


J'aurais tendance à dire plutôt « fonction() != 0 ».
Yes.

En fait, je pensais:
bool condition;
...
condition = !!fonction();
mais effectivement,
condition = (fonction != 0);
est plus sobre et plus efficace...

Pour le reste, il est donc clair que bool dans le langage ou pas (puisque
C++ ne nous aide pas beaucoup) c'est du kif. Tout est dans les
auto-contraintes.
Sur tous les compilos (Borland de TC++3 à C++builder 6, VC++6), seul un GCC
me fournit un stdbool.h.
Il définit le type par un enum, mais définit également les macros true et
false (pour éviter des collisions, j'imagine). Pour info :
<stdbool.h>
/* stdbool.h for GNU. */
#ifndef __STDBOOL_H__
#define __STDBOOL_H__ 1

/* The type `bool' must promote to `int' or `unsigned int'. The constants
`true' and `false' must have the value 0 and 1 respectively. */
typedef enum
{
false = 0,
true = 1
} bool;

/* The names `true' and `false' must also be made available as macros. */
#define false false
#define true true

/* Signal that all the definitions are present. */
#define __bool_true_false_are_defined 1

#endif /* stdbool.h */
</stdbool.h>

Cordialement,
Pierre


Avatar
Michel Michaud
Dans news:3f8c6ed3$0$20642$, Pierre
- En C :
typedef enum (false, true) bool;
typedef enum (false, true = !false) bool;


Non, car

bool F(int a, int b)
{
return a == b;
}

ne compilera pas. La solution C est vraiment un bool === int
et des constantes numériques pour true/false, car c'est ça que
donne les opérateurs relationnels (à moins que ce soit changé
dans C99 ?).

En C++, les opérateurs relationnels donnent des bool et les
conditions de if/while/etc. doivent être bool aussi (ou pouvant
être converties).

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

Avatar
Loïc Joly
Pierre Maurette wrote:

Pour le reste, il est donc clair que bool dans le langage ou pas (puisque
C++ ne nous aide pas beaucoup) c'est du kif. Tout est dans les
auto-contraintes.


Pas uniquement : L'existence d'un type bool permet aussi de surcharger
une fonction entre un bool et un int, par exemple.

--
Loïc

Avatar
kanze
Loïc Joly wrote in message
news:<bmk3um$955$...
Pierre Maurette wrote:

Pour le reste, il est donc clair que bool dans le langage ou pas
(puisque C++ ne nous aide pas beaucoup) c'est du kif. Tout est dans
les auto-contraintes.


Pas uniquement : L'existence d'un type bool permet aussi de surcharger
une fonction entre un bool et un int, par exemple.


Surtout, il offre une solution standard. Ça fait vingt ans que je fais
du C ou du C++, et je n'ai jamais fait un projet où il n'y avait pas de
typedef pour bool (ou Bool, ou bool_t, ou ...) et des #define/const pour
true et false (ou TRUE et FALSE). Dans chaque projet, en revanche, ils
étaient différent -- dans le projet sur lequel je travaille
actuellement, il y en a au moins trois, d'ailleurs, du fait que le code
vient des sources différentes, ou a été écrit à des moments différents
par des gens différents.

Du coup, chaque fois que je devais commencer un nouveau projet, je
devais apprendre leurs conventions par rapport aux types booléens.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16


Avatar
kanze
Marc Boyer wrote in message
news:<bmirg6$1vk$...
wrote:
"Pierre Maurette" wrote in message
news:<3f8bb008$0$20153$...
On peut sans problème comparer à false (if(C!úlse) est correct),
mais jamais à true, ni à un autre bool.


On ne compare jamais un bool à une constante. En revanche, la
comparaison de deux bools est bien définis par la norme, et ce que
tu as vu avec Borland est une erreur.


Peux-tu préciser, j'ai du mal avec "constante". Tu veux dire qu'on
ne doit pas faire C != false ?


Exact. Logiquement, la condition d'un if, while, etc. est une valeur
booléene. Si on a une valeur logiquement booléene, on s'en sert
directement. Si on n'en a pas, on fait une comparaison pour en obtenir
une.

Après tout, le résultat de « c != false », c'est bien une valeur
booléene lui aussi. Alors, est-ce qu'il faut écrire « (c != false) = true » ?

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16



Avatar
Marc Boyer
wrote:
Marc Boyer wrote in message
news:<bmirg6$1vk$...
Peux-tu préciser, j'ai du mal avec "constante". Tu veux dire qu'on
ne doit pas faire C != false ?


Exact. Logiquement, la condition d'un if, while, etc. est une valeur
booléene. Si on a une valeur logiquement booléene, on s'en sert
directement. Si on n'en a pas, on fait une comparaison pour en obtenir
une.

Après tout, le résultat de « c != false », c'est bien une valeur
booléene lui aussi. Alors, est-ce qu'il faut écrire « (c != false) = > true » ?


Disons que je n'avais pas rencontré le problème, parce que
globalement, j'utilise les booleens comme tu les présentes. Mais
en tant qu'enseignant, je pourrais plus rencontrer le problème
(enfin, le jour ou j'enseignerai C++)

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(


1 2