I. Révisions▲
Version Boost |
Révision article |
---|---|
1.36 |
Rédaction |
1.37 |
Pas de modification |
1.38 |
Pas de modification |
1.39 |
Pas de modification |
II. Avant-propos▲
Boost se veut un ensemble de bibliothèques portables destinées à faciliter la vie du développeur C++. Boost se propose de construire du code de référence pouvant aussi, à terme, être incorporé au standard (STL). Pour plus d'informations, vous pouvez consulter :
- les différents tutoriels Boost de développez.com : Tutoriels ;
- la F.A.Q Boost de développez.com : F.A.Q. ;
- le site officiel de Boost : Boost ;
- le site officiel de la bibliothèque : Boost.Tribool.
III. Remerciements▲
Je remercie Alp pour ses encouragements et sa relecture, RideKick pour sa relecture finale et fais allégeance à hiko-seijuro mon parrain…
Enfin, d'une façon plus globale, je remercie les membres des forums de développez.com qui par la qualité de leurs interventions m'ouvrent constamment de nouvelles pistes de réflexion sur ma pratique de développement.
IV. Introduction▲
IV-A. Prérequis▲
Boost.Tribool est une des bibliothèques les plus simples de Boost. Aucune connaissance préalable n'est nécessaire. Le lecteur doit être familiarisé avec le langage C++. La bibliothèque s'interface avec les flux. Une connaissance minimale de ceux-ci est nécessaire, ainsi que des aspects locale. Le lecteur peut consulter sur le site de developpez.com les différents tutoriels sur les flux et sur locale. L'utilisation de la bibliothèque Boost.Tribool ne nécessite pas de connaissances particulières sur les autres bibliothèques Boost.
Pour une classe simple, une implémentation simple : sa compréhension ne nécessite pas un niveau de connaissance plus important que son utilisation.
IV-B. Objectifs▲
Boost.Tribool, comme son nom l'indique, étend le type bool avec une troisième valeur. Cela permet d'avoir des variables prenant les valeurs 'vrai', 'faux' ou 'indéterminé'. Pourquoi faire compliqué alors qu'une simple énumération suffirait ? La classe offre plus qu'un simple enum en surchargeant opérateurs et fonctions adéquats.
IV-C. Convention▲
On notera true ou false pour désigner ces valeurs telles qu'elles sont définies en C++ avec le type bool. On notera 'vrai', 'faux' ou 'indéterminé' pour les trois valeurs que peut prendre une instance de tribool.
V. Premiers pas▲
V-A. Une variable Boost.Tribool▲
La déclaration d'une variable Boost.Tribool se fait comme suit :
boost::logic::
tribool test;
En l'absence d'initialisation explicite, la variable est initialisée par défaut à la valeur 'faux' et non 'indéterminé'.
Pour initialiser la variable à la valeur 'vrai' ou 'faux', on se sert simplement du type booléen C++ :
boost::logic::
tribool test;
test =
true
; // la variable a comme valeur 'vrai'
test =
false
; // la variable a comme valeur 'faux'
boost::logic::
tribool vrai(true
); // variable explicitement initialisée à 'vrai'
boost::logic::
tribool faux(false
); // variable explicitement initialisée à 'vrai'
V-B. La valeur indéterminée▲
Pour initialiser une variable Boost.Tribool à la valeur 'indéterminé', on peut utiliser le mot-clé indeterminate proposé par la bibliothèque :
boost::logic::
tribool test;
test =
boost::logic::
indeterminate;
On peut aussi définir son propre mot-clé pour cette valeur :
BOOST_TRIBOOL_THIRD_STATE(peut_etre);// définition d'un
// mot-clé spécifique pour la valeur 'indéterminé'
int
main()
{
boost::logic::
tribool test;
test =
peut_etre; // utilisation de notre mot-clé
return
1
;
}
Attention, un mot-clé ne peut être défini localement à une fonction ou une méthode :
void
MaFonction()
{
BOOST_TRIBOOL_THIRD_STATE(peut_etre); // Erreur de compilation
}
void
A::
MaMethode()
{
BOOST_TRIBOOL_THIRD_STATE(peut_etre); // Erreur de compilation
}
V-C. Tester la valeur▲
'vrai', c'est vrai ! 'faux' n'est pas vrai ! 'indéterminé' ? Et bien, 'indéterminé' n'est pas vrai :
boost::logic::
tribool test;
// ...
if
(test){
std::
cout<<
"vrai!"
<<
std::
endl;
}
else
{
std::
cout<<
"faux ou peut-être!"
<<
std::
endl;
}
Ce qui n'est pas 'faux' est vrai ! Ce qui n'est pas 'vrai' est faux ! Ce qui n'est pas 'indéterminé' est … indéterminé (donc n'est pas vrai) :
boost::logic::
tribool test;
// ...
if
(!
test){
std::
cout<<
"faux!"
<<
std::
endl;
}
else
{
std::
cout<<
"vrai ou peut-être!"
<<
std::
endl;
}
Enfin, pour savoir si une variable est 'indéterminé', on utilise le mot-clé comme fonction :
boost::logic::
tribool test;
// ...
if
(boost::logic::
indeterminate(test)){
std::
cout<<
"indéterminé!"
<<
std::
endl;
}
else
{
std::
cout<<
"vrai ou faux!"
<<
std::
endl;
}
Les mot-clés définis précédemment peuvent aussi être utilisés en tant que fonction :
BOOST_TRIBOOL_THIRD_STATE(peut_etre);
int
main()
{
boost::logic::
tribool test;
// ...
if
(peut_etre(test)){
std::
cout<<
"indéterminé!"
<<
std::
endl;
}
else
{
std::
cout<<
"vrai ou faux!"
<<
std::
endl;
}
//...
V-D. Comparaison n'est pas raison▲
Essayons le code suivant :
boost::logic::
tribool var_1(true
);
boost::logic::
tribool var_2(true
);
if
(var_1==
var_2){
std::
cout<<
"Jusqu'ici tout va bien"
<<
std::
endl;
}
Jusqu'ici tout va bien !
Maintenant, modifions légèrement :
boost::logic::
tribool var_1(false
);
boost::logic::
tribool var_2(false
);
if
(var_1==
var_2){
std::
cout<<
"Jusqu'ici tout va bien"
<<
std::
endl;
}
Jusqu'ici tout va bien !
L'atterrissage :
boost::logic::
tribool var_1(boost::logic::
indeterminate);
boost::logic::
tribool var_2(boost::logic::
indeterminate);
if
(var_1==
var_2){
std::
cout<<
"Jusqu'ici tout va bien"
<<
std::
endl;
}
else
{
std::
cout<<
"Aïe !"
<<
std::
endl;
}
Aïe !
Il est préférable de ne pas utiliser l'opérateur '==' pour évaluer si deux variables sont dans le même état.
À noter que l'opérateur '!=' pour maintenir la cohérence réserve les mêmes surprises :
boost::logic::
tribool vrai(true
);
boost::logic::
tribool faux(false
);
boost::logic::
tribool indetermine(boost::logic::
indeterminate);
if
(vrai!=
indetermine){
std::
cout<<
"OK vrai != indéterminé"
<<
std::
endl;
}
else
{
std::
cout<<
"vrai != indéterminé vaut false ! "
<<
std::
endl;
}
if
(vrai!=
false
){
std::
cout<<
"OK vrai != false"
<<
std::
endl;
}
else
{
std::
cout<<
"vrai != false vaut false ! "
<<
std::
endl;
}
if
(vrai!=
true
){
std::
cout<<
"vrai != true vaut true ! "
<<
std::
endl;
}
else
{
std::
cout<<
"OK vrai != true vaut false"
<<
std::
endl;
}
if
(faux!=
indetermine){
std::
cout<<
"OK faux != indéterminé"
<<
std::
endl;
}
else
{
std::
cout<<
"faux != indéterminé vaut false ! "
<<
std::
endl;
}
if
(faux!=
false
){
std::
cout<<
"faux != false vaut true ! "
<<
std::
endl;
}
else
{
std::
cout<<
"OK faux != false vaut false"
<<
std::
endl;
}
if
(indetermine!=
boost::logic::
indeterminate){
std::
cout<<
"OK indéterminé != boost::logic::indeterminate"
<<
std::
endl;
}
else
{
std::
cout<<
"indéterminé != boost::logic::indeterminate vaut false ! "
<<
std::
endl;
}
Ce code produit la sortie suivante :
vrai != indéterminé vaut false !
OK vrai != false
OK vrai != true vaut false
faux != indéterminé vaut false !
OK faux != false vaut false
indéterminé != boost::logic::indeterminate vaut false !
V-E. Accéder à la valeur▲
Une variable Boost.Tribool contient un membre value d'un type énuméré permettant de résoudre le problème présenté ci-dessus :
boost::logic::
tribool var;
//...
switch
(var.value){
case
boost::logic::tribool::
true_value:
std::
cout<<
"vrai !"
<<
std::
endl;
break
;
case
boost::logic::tribool::
false_value:
std::
cout<<
"faux !"
<<
std::
endl;
break
;
case
boost::logic::tribool::
indeterminate_value:
std::
cout<<
"indéterminé !"
<<
std::
endl;
break
;
}
V-F. Écrire une variable Boost.Tribool vers un flux de sortie▲
L'envoi d'un booléen sur un flux produit soit une sortie numérique (0 ou 1) soit une sortie texte (par simplicité, disons true ou false) en fonction des drapeaux définis :
- std::noboolalpha : produit une sortie numérique ;
- std::boolalpha : produit une sortie sous forme de texte.
Boost.Tribool utilise ces deux drapeaux pour réaliser la sortie d'une de ses variables sous forme numérique ou sous forme textuelle :
std::
cout<<
std::
noboolalpha;
std::
cout<<
"vrai = "
<<
boost::logic::
tribool(true
)<<
std::
endl;
std::
cout<<
"faux = "
<<
boost::logic::
tribool(false
)<<
std::
endl;
std::
cout<<
"indéterminé = "
<<
boost::logic::
tribool(boost::logic::
indeterminate)<<
std::
endl;
std::
cout<<
std::
boolalpha;
std::
cout<<
"vrai = "
<<
boost::logic::
tribool(true
)<<
std::
endl;
std::
cout<<
"faux = "
<<
boost::logic::
tribool(false
)<<
std::
endl;
std::
cout<<
"indéterminé = "
<<
boost::logic::
tribool(boost::logic::
indeterminate)<<
std::
endl;
La sortie produite est :
vrai =
1
faux =
0
indéterminé =
2
vrai =
true
faux =
false
indéterminé =
indeterminate
On peut personnaliser la sortie sous forme de texte pour la valeur 'indéterminé' :
std::
locale mon_locale(// on créé une locale
std::
locale(""
), // avec la locale
new
boost::logic::
indeterminate_name<
char
>
("peut-être"
)// c'est ici la ligne cruciale
// on rajoute une facette qui se charge de définir le nom utilisé
);
std::
cout.imbue(mon_locale);// on rajoute la facette au flux de sortie
std::
cout<<
std::
boolalpha;
std::
cout<<
"indéterminé = "
<<
boost::logic::
tribool(boost::logic::
indeterminate)<<
std::
endl;
La sortie obtenue devient :
indéterminé =
peut-
être
V-G. Lire une variable Boost.Tribool depuis un flux d'entrée▲
À l'instar de l'écriture, la lecture peut se faire avec les valeurs numériques :
boost::logic::
tribool test;
std::
stringstream("1"
)>>
std::
noboolalpha>>
test;// État 'vrai'
std::
stringstream("0"
)>>
std::
noboolalpha>>
test;// État 'faux'
std::
stringstream("2"
)>>
std::
noboolalpha>>
test;// État 'indéterminé'
Si une valeur autre que 0, 1 ou 2 est lue depuis le flux, le flux est marqué en erreur : std::ios_base::failbit positionné :
boost::logic::
tribool test;
std::
cin>>
test;
if
(std::
cin.fail()){
std::
cout<<
"Saisir une valeur valide (0, 1 ou 2)"
<<
std::
endl;
}
Et la lecture peut se faire avec les labels :
boost::logic::
tribool test;
std::
stringstream("true"
)>>
std::
boolalpha>>
test;// État 'vrai'
std::
stringstream("false"
)>>
std::
boolalpha>>
test;// État 'faux'
std::
stringstream("indeterminate"
)>>
std::
boolalpha>>
test;// État 'indéterminé'*/
Un label particulier peut être défini pour la lecture :
std::
locale mon_locale(// on créé une locale
std::
locale(""
), // avec la locale
new
boost::logic::
indeterminate_name<
char
>
("peut-être"
)// c'est ici la ligne cruciale
// on rajoute une facette qui se charge de définir le nom utilisé
);
boost::logic::
tribool test;
std::
stringstream strstr("peut-être"
);
strstr.imbue(mon_locale);
strstr>>
std::
boolalpha>>
test;
Comme pour les valeurs numériques, si la valeur littérale n'est pas reconnue, le flux est marqué en erreur en positionnant std::ios_base::failbit.
VI. Utilisation de Boost.Tribool▲
VI-A. En-têtes▲
Commençons par l'essentiel : les fichiers d'en-têtes nécessaires :
- boost/logic/tribool.hpp ;
- boost/logic/tribool_io.hpp.
Le premier en-tête contient la définition de la classe ainsi que celles des fonctions et opérateurs surchargés. Le second fichier d'en-tête offre l'interface nécessaire à l'utilisation de cette nouvelle classe avec les entrées/sorties.
VI-B. Espace de nommage▲
L'espace de nommage est :
namespace
boost::
logic
//ou
namespace
boost
Dans ce document, nous utiliserons systématiquement l'espace de nommage complet : boost::logic::XXX.
VI-C. Le type boost::logic::tribool▲
VI-C-1. La classe▲
class
tribool {
public
:
enum
value_t;
// Constructeurs:
tribool();
tribool(bool
);
tribool(indeterminate_keyword_t);
// Opérateur de conversion
operator
safe_bool() const
;
// valeur
enum
boost::logic::tribool::
value_t value;
}
;
-
tribool() :
- Objectif : Constructeur par défaut ;
- Postcondition : La valeur prise est 'faux' ;
- Exceptions : Aucune.
-
tribool(bool) :
- Objectif : Constructeur.
- Postcondition : La valeur prise est 'vrai' si le paramètre est true, et 'faux' si le paramètre est false.
- Exceptions : Aucune.
-
tribool(indeterminate_keyword_t) :
- Objectif : Constructeur.
- Postcondition : La valeur prise est 'indéterminé'.
- Notes : Nous verrons plus loin quel est ce paramètre 'indéterminé'.
-
operator safe_bool() const :
- Objectif : Opérateur de conversion pour tester la valeur.
- Postcondition : La valeur en retour s'évalue à true uniquement si this est dans l'état 'vrai'.
- Exceptions : Aucune.
-
boost::logic::tribool::value_t value :
- Objectif : L'état de l'objet à l'aide d'un enum …
- Définition : enum value_t { false_value, true_value, indeterminate_value };.
- Notes : Bien qu'exposée dans l'interface de la classe, on peut dans la plupart des cas se passer de cette valeur. L'utilisation la plus opportune est dans un switch/case. On notera qu'aucun constructeur ne prend cet enum en paramètre. Tout simplement, car les valeurs 'vrai' et 'faux' sont fixées par le passage d'un bool et la valeur 'indéterminé' utilise le type particulier indeterminate_keyword_t abordé plus bas.
L'état pris par le constructeur par défaut est 'faux' et non 'indéterminé', ce qui n'est pas forcément intuitif.
VI-C-2. Type 'indéterminé'▲
L'identificateur pour l'état 'indéterminé' est :
indeterminate
On peut donc l'utiliser comme suit :
boost::logic::
tribool ma_variable(boost::logic::
indeterminate);
// ou
ma_variable =
boost::logic::
indeterminate;
Une macro permet de définir son propre mot-clé pour l'état 'indéterminé' :
BOOST_TRIBOOL_THIRD_STATE(Name)
Ce qui donne :
BOOST_TRIBOOL_THIRD_STATE(peut_etre);
int
main()
{
boost::logic::
tribool ma_variable(peut_etre);
...
boost::logic::indeterminate ou les mot-clés définis avec BOOST_TRIBOOL_THIRD_STATE correspondent aussi à des fonctions permettant de tester si une variable tribool est dans l'état 'indéterminé' :
BOOST_TRIBOOL_THIRD_STATE(peut_etre)
int
main()
{
boost::logic::
tribool ma_variable(peut_etre);
if
(peut_etre(ma_variable)){
std::
cout<<
"peut_etre"
<<
std::
endl;
}
if
(boost::logic::
indeterminate(ma_variable)){
std::
cout<<
"indeterminate"
<<
std::
endl;
}
return
0
;
}
-
inline bool indeterminate(tribool x);
- Objectif : Tester si le paramètre est dans l'état 'indéterminé'.
- Paramètre : La valeur à tester.
- Postcondition : La fonction retourne true si le paramètre est dans l'état 'indéterminé', false sinon.
- Exceptions : Aucune.
- Note : Ceci s'applique aussi aux fonctions définies par BOOST_TRIBOOL_THIRD_STATE.
VI-C-3. Opérateur !▲
tribool operator !(tribool x); :
- Objectif : Surcharge de l'opérateur '!'.
-
Postcondition :
État du paramètre
État retourné
'vrai'
'faux'
'faux'
'vrai'
'indéterminé'
'indéterminé'
-
Exceptions : Aucune.
- Note : On remarque que l'état 'indéterminé' est neutre pour l'opérateur.
VI-C-4. Opérateur &&▲
tribool operator &&(tribool x, indeterminate_keyword_t);
tribool operator &&(tribool x, tribool y);
tribool operator &&(tribool x, bool y);
tribool operator &&(bool x, tribool y);
tribool operator &&(indeterminate_keyword_t, tribool x);
- Objectif : Surcharge de l'opérateur '&&'.
-
Postcondition :
État opérande gauche
État opérande droite
État retourné
'vrai'/true
'vrai'/true
'vrai'
'vrai'/true
'faux'/false
'faux'
'vrai'/true
'indéterminé'
'indéterminé'
'faux'/false
'vrai'/true
'faux'
'faux'/false
'faux'/false
'faux'
'faux'/false
'indéterminé'
'faux'
'indéterminé'
'vrai'/true
'indéterminé'
'indéterminé'
'faux'/false
'faux'
'indéterminé'
'indéterminé'
'indéterminé'
-
Exceptions : Aucune.
-
Note 1 : on remarque que l'état 'faux' est absorbant.
-
Note 2 : les différentes surcharges permettent l'utilisation de l'opérateur avec un bool ou avec un mot-clé de type indeterminate_keyword_t.
- Note 3 : le mot-clé de type indeterminate_keyword_t ne peut être utilisé que conjointement avec un tribool. L'utilisation directe d'un indeterminate_keyword_t avec un bool génère un avertissement.
VI-C-5. Opérateur ||▲
tribool operator ||(tribool x, tribool y);
tribool operator ||(tribool x, bool y);
tribool operator ||(bool x, tribool y);
tribool operator ||(indeterminate_keyword_t, tribool x);
tribool operator ||(tribool x, indeterminate_keyword_t);
- Objectif : Surcharge de l'opérateur '||'.
-
Postcondition :
État opérande gauche
État opérande droite
État retourné
'vrai'/true
'vrai'/true
'vrai'
'vrai'/true
'faux'/false
'vrai'
'vrai'/true
'indéterminé'
'vrai'
'faux'/false
'vrai'/true
'vrai'
'faux'/false
'faux'/false
'faux'
'faux'/false
'indéterminé'
'indéterminé'
'indéterminé'
'vrai'/true
'vrai'
'indéterminé'
'faux'/false
'indéterminé'
'indéterminé'
'indéterminé'
'indéterminé'
-
Exceptions : Aucune.
-
Note 1 : On remarque que l'état 'vrai' est absorbant. Les règles des opérateurs sont cohérentes : a||b == !((!a)&&(!b)).
-
Note 2 : Les différentes surcharges permettent l'utilisation de l'opérateur avec un bool ou avec un mot-clé de type indeterminate_keyword_t.
- Note 3 : Le mot-clé de type indeterminate_keyword_t ne peut être utilisé que conjointement avec un tribool. L'utilisation directe d'un indeterminate_keyword_t avec un bool génère un avertissement.
VI-C-6. Opérateur ==▲
tribool operator ==(tribool x, tribool y);
tribool operator ==(tribool x, bool y);
tribool operator ==(bool x, tribool y);
tribool operator ==(indeterminate_keyword_t, tribool x);
tribool operator ==(tribool x, indeterminate_keyword_t);
- Objectif : Surcharge de l'opérateur '=='.
-
Postcondition :
État opérande gauche
État opérande droite
État retourné
'vrai'/true
'vrai'/true
'vrai'
'vrai'/true
'faux'/false
'faux'
'vrai'/true
'indéterminé'
'indéterminé'
'faux'/false
'vrai'/true
'faux'
'faux'/false
'faux'/false
'vrai'
'faux'/false
'indéterminé'
'indéterminé'
'indéterminé'
'vrai'/true
'indéterminé'
'indéterminé'
'faux'/false
'indéterminé'
'indéterminé'
'indéterminé'
'indéterminé'
-
Exceptions : Aucune.
-
Note 1 : On remarque que l'état 'indéterminé' est absorbant.
Attention, cela signifie que les tests if('indéterminé'=='indéterminé') échouent !
-
Note 2 : Les différentes surcharges permettent l'utilisation de l'opérateur avec un bool ou avec un mot-clé de type indeterminate_keyword_t.
- Note 3 : Le mot-clé de type indeterminate_keyword_t ne peut être utilisé que conjointement avec un tribool. L'utilisation directe d'un indeterminate_keyword_t avec un bool génère un avertissement.
VI-C-7. Opérateur !=▲
tribool operator !=(tribool x, tribool y);
tribool operator !=(tribool x, bool y);
tribool operator !=(bool x, tribool y);
tribool operator !=(indeterminate_keyword_t, tribool x);
tribool operator !=(tribool x, indeterminate_keyword_t);
- Objectif : Surcharge de l'opérateur '!='.
-
Postcondition :
État opérande gauche
État opérande droite
État retourné
'vrai'/true
'vrai'/true
'faux'
'vrai'/true
'faux'/false
'vrai'
'vrai'/true
'indéterminé'
'indéterminé'
'faux'/false
'vrai'/true
'vrai'
'faux'/false
'faux'/false
'faux'
'faux'/false
'indéterminé'
'indéterminé'
'indéterminé'
'vrai'/true
'indéterminé'
'indéterminé'
'faux'/false
'indéterminé'
'indéterminé'
'indéterminé'
'indéterminé'
-
Exceptions : Aucune.
-
Note 1 : On remarque que l'état 'indéterminé' est absorbant.
Attention, cela signifie que les tests if('indéterminé'!='vrai'/true), if('indéterminé'!='faux'/false) et if('indéterminé'!='indéterminé') échouent !
La cohérence est assurée : 'l!=r' prendra la même valeur que '!(l==r)'.
-
Note 2 : Les différentes surcharges permettent l'utilisation de l'opérateur avec un bool ou avec un mot-clé de type indeterminate_keyword_t.
- Note 3 : Le mot-clé de type indeterminate_keyword_t ne peut être utilisé que conjointement avec un tribool. L'utilisation directe d'un indeterminate_keyword_t avec un bool génère un avertissement.
VI-D. Les flux et boost::logic::tribool▲
VI-D-1. Les valeurs▲
Pour rappel, les valeurs bool existent sous deux formats dans les flux. Elles peuvent être numériques ou littérales. Soit l'exemple suivant :
#include
<iostream>
int
main()
{
std::
cout<<
std::
noboolalpha;
std::
cout<<
true
<<
std::
endl;
std::
cout<<
false
<<
std::
endl;
std::
cout<<
std::
boolalpha;
std::
cout<<
true
<<
std::
endl;
std::
cout<<
false
<<
std::
endl;
return
0
;
}
Ce programme produit la sortie suivante :
Valeur 'true' : 1
Valeur 'false' : 0
Litteral 'true' : true
Litteral 'false' : false
De la même façon, une valeur tribool peut être utilisée dans les flux soit sous forme numérique, soit sous forme littérale. Lorsque l'état d'une valeur tribool correspond à 'vrai' ou à 'faux' alors ce sont les paramètres positionnés pour bool qui sont pris en compte. La classe ne gère que l'état 'indéterminé'. La valeur numérique par défaut est '2' et la valeur littérale par défaut est 'indeterminate'. Le code suivant :
#include
<iostream>
#include
<boost/logic/tribool.hpp>
#include
<boost/logic/tribool_io.hpp>
int
main()
{
boost::logic::
tribool ma_variable;
std::
cout<<
std::
noboolalpha;
std::
cout<<
"vrai : "
<<
boost::logic::
tribool(true
)<<
std::
endl;
std::
cout<<
"faux : "
<<
boost::logic::
tribool(false
)<<
std::
endl;
std::
cout<<
"indéterminé : "
<<
boost::logic::
tribool(boost::logic::
indeterminate)<<
std::
endl;
std::
cout<<
std::
boolalpha;
std::
cout<<
"vrai : "
<<
boost::logic::
tribool(true
)<<
std::
endl;
std::
cout<<
"faux : "
<<
boost::logic::
tribool(false
)<<
std::
endl;
std::
cout<<
"indéterminé : "
<<
boost::logic::
tribool(boost::logic::
indeterminate)<<
std::
endl;
return
0
;
}
produira comme sortie :
vrai : 1
faux : 0
indéterminé : 2
vrai : true
faux : false
indéterminé : indeterminate
VI-D-2. Entrée▲
L'opérateur '>>' est surchargé pour le type boost::logic::tribool.
template
<
typename
CharT, typename
Traits>
inline
std::
basic_istream<
CharT, Traits>&
operator
>>
(std::
basic_istream<
CharT, Traits>&
in, tribool&
x);
- Objectif : Surcharge de l'opérateur '>>'.
- Postcondition : Si la valeur lue est valide, alors la variable est dans l'état correspondant. Sinon, le flag failbit est positionné dans le flux. Le flux est retourné.
- Exceptions : Dépend de ios::exceptions (voir section sur l'implémentation).
La lecture d'une valeur se fait simplement :
#include
<iostream>
#include
<sstream>
#include
<boost/logic/tribool.hpp>
#include
<boost/logic/tribool_io.hpp>
int
main()
{
boost::logic::
tribool ma_variable;
std::
stringstream("1"
)>>
std::
noboolalpha>>
ma_variable;// État 'vrai'
std::
stringstream("0"
)>>
std::
noboolalpha>>
ma_variable;// État 'faux'
std::
stringstream("2"
)>>
std::
noboolalpha>>
ma_variable;// État 'indéterminé'
std::
stringstream("true"
)>>
std::
boolalpha>>
ma_variable;// État 'vrai'
std::
stringstream("false"
)>>
std::
boolalpha>>
ma_variable;// État 'faux'
std::
stringstream("indeterminate"
)>>
std::
boolalpha>>
ma_variable; // État 'indéterminé'
return
0
;
}
Si le flux n'est pas paramétré avec std::boolalpha, alors les valeurs attendues sont 0 pour 'faux', 1 pour 'vrai' et 2 pour 'indéterminé'.
Si le flux est paramétré avec std::boolalpha et si l'implémentation de la STL n'implémente pas locale, alors les valeurs attendues sont 'false' pour 'faux', 'true' pour 'vrai' et 'indeterminate' pour 'indéterminé'. Ces chaînes sont définies par la bibliothèque Boost.Tribool.
Si le flux est paramétré avec std::boolalpha et si l'implémentation de la STL implémente locale, alors les valeurs attendues sont celles de la facette std::numpunct du flux, soit std::numpunct<CharT>::falsename() pour 'faux', std::numpunct<CharT>::truename() pour 'vrai'. Pour l'état 'indéterminé', soit une facette spécifique existe (cf. infra.), et elle est utilisée, soit il s'agit de la valeur par défaut 'indeterminate' (définie par la bibliothèque Boost.Tribool).
VI-D-3. Sorties▲
Comme le laisse entendre la section sur les valeurs, l'opérateur '<<' est surchargé pour le type boost::logic::tribool.
template
<
typename
CharT, typename
Traits>
inline
std::
basic_ostream<
CharT, Traits>&
operator
<<
(std::
basic_ostream<
CharT, Traits>&
out, tribool x)
- Objectif : Surcharge de l'opérateur '<<'.
- Postcondition : Le flux est retourné.
- Exceptions : Dépend de ios::exceptions (voir section sur l'implémentation).
L'écriture se fait simplement :
#include
<iostream>
#include
<boost/logic/tribool.hpp>
#include
<boost/logic/tribool_io.hpp>
int
main()
{
std::
cout<<
std::
noboolalpha;
std::
cout<<
"vrai : "
<<
boost::logic::
tribool(true
)
<<
std::
endl;
std::
cout<<
"faux : "
<<
boost::logic::
tribool(false
)
<<
std::
endl;
std::
cout<<
"indéterminé : "
<<
boost::logic::
tribool(boost::logic::
indeterminate)
<<
std::
endl;
std::
cout<<
std::
boolalpha;
std::
cout<<
"vrai : "
<<
boost::logic::
tribool(true
)
<<
std::
endl;
std::
cout<<
"faux : "
<<
boost::logic::
tribool(false
)
<<
std::
endl;
std::
cout<<
"indéterminé : "
<<
boost::logic::
tribool(boost::logic::
indeterminate)
<<
std::
endl;
return
0
;
}
La sortie est gérée pour le type du mot-clé 'indéterminé' indeterminate_keyword_t :
#include
<boost/logic/tribool.hpp>
#include
<boost/logic/tribool_io.hpp>
#include
<iostream>
BOOST_TRIBOOL_THIRD_STATE(peut_etre)
using
boost::logic::
operator
<<
;
int
main()
{
std::
cout<<
"peut_etre : "
<<
std::
boolalpha
<<
peut_etre
<<
" ( "
<<
std::
noboolalpha<<
peut_etre<<
" )"
<<
std::
endl;
std::
cout<<
"peut_etre : "
<<
std::
boolalpha
<<
boost::logic::
indeterminate
<<
" ( "
<<
std::
noboolalpha<<
boost::logic::
indeterminate<<
" )"
<<
std::
endl;
return
0
;
}
L'opérateur << doit être importé dans l'espace de nommage courant, soit par le biais de 'using boost::logic::operator <<;', soit par le biais de 'using namespace boost::logic;'. Autrement, certains compilateurs (Visual C+ 2008 Express Edition, par exemple) prennent une autre surcharge de l'opérateur.
Si l'état est 'vrai' ou 'faux' alors la surcharge de l'opérateur est strictement équivalente à out << true; ou respectivement out << false;.
Pour l'état 'indéterminé'
Si le flux n'est pas paramétré avec std::boolalpha, alors la valeur produite est 2.
Si le flux est paramétré avec std::boolalpha et si l'implémentation de la STL n'implémente pas locale, alors la valeur produite est 'indeterminate' (définie par la bibliothèque Boost.Tribool).
Si le flux est paramétré avec std::boolalpha et si l'implémentation de la STL implémente locale, et si la facette spécifique (cf. infra.) est positionnée pour le flux, alors la valeur produite est celle de la facette.
Si le flux est paramétré avec std::boolalpha et si l'implémentation de la STL implémente locale, et le flux n'a pas de facette spécifique, alors la valeur produite est 'indeterminate' (définie par la bibliothèque Boost.Tribool).
.
VI-D-4. Facette pour l'état 'indéterminé'▲
En l'absence de facette ou en l'absence d'implémentation de locale par la STL, la fonction suivante est utilisée pour récupérer la valeur littérale de l'état 'indéterminé' :
template
<
typename
T>
std::
basic_string<
T >
get_default_indeterminate_name();
Cette fonction fait partie de l'interface et peut, par conséquent, être utilisée pour récupérer le nom par défaut des développeurs de Boost.
Mais, pour correctement internationaliser un programme, il est préférable, si la STL implémente locale d'utiliser une facette. Boost.Tribool définit une classe pour cette facette dont l'interface est :
template
<
typename
CharT>
class
indeterminate_name {
public
:
// types
typedef
CharT char_type;
typedef
std::
basic_string<
CharT >
string_type;
// construct/copy/destruct
indeterminate_name();
indeterminate_name(const
string_type &
);
// public member functions
string_type name() const
;
static
std::locale::
id id;
}
;
Attention, il s'agit de l'interface publique et non de sa réelle implémentation (cf. infra.)
La seule chose à faire est d'instancier une variable de cette classe et de la positionner dans le flux. Deux constructeurs sont possibles. Le constructeur par défaut renseignera la valeur avec la fonction précédente get_default_indeterminate_name. Le second constructeur permet de spécifier sa propre chaîne.
L'exemple ci-dessous, sous réserve que la STL implémente locale, permet de positionner « peut-être » pour la valeur 'indéterminé' :
#include
<boost/logic/tribool.hpp>
#include
<boost/logic/tribool_io.hpp>
#include
<iostream>
#include
<sstream>
BOOST_TRIBOOL_THIRD_STATE(peut_etre)
using
boost::logic::
operator
<<
;
int
main()
{
// spécialisation de locale avec notre facette :
// la facette est détruite lorsque mon_locale est détruite.
std::
locale mon_locale(std::
locale(), new
boost::logic::
indeterminate_name<
char
>
("peut-être"
));
// positionnement de nos paramètres locaux dans notre flux :
std::
cout.imbue(mon_locale);
// sortie :
std::
cout<<
"peut_etre : "
<<
std::
boolalpha<<
boost::logic::
tribool(peut_etre)<<
std::
endl;
std::
cout<<
"peut_etre : "
<<
std::
boolalpha<<
peut_etre<<
std::
endl;
boost::logic::
tribool ma_variable;
std::
stringstream mon_flux("peut-être"
);
mon_flux.imbue(mon_locale);
mon_flux>>
std::
boolalpha>>
ma_variable; // État 'indéterminé'
return
0
;
}
VI-D-5. STL sans implémentation de locale▲
Si la STL n'implémente pas locale, Boost.Tribool doit être utilisé avec la directive de compilation BOOST_NO_STD_LOCALE.
VI-E. En résumé▲
En-tête de la classe : |
boost/logic/tribool.hpp |
En-tête i/o : |
boost/logic/tribool_io.hpp |
Espace de nommage : |
boost::logic |
Définition d'une variable : |
boost::logic::tribool ma_variable; |
Mot-clé pour 'indéterminé' : |
boost::logic::indeterminate |
Définition de son propre mot-clé : |
BOOST_TRIBOOL_THIRD_STATE(Name) |
Tester l'état 'indéterminé' : |
if(boost::logic::indeterminate(ma_variable)) |
Valeur par défaut dans un flux : |
État 'faux' >> 'false' >> 0 |
Le constructeur par défaut positionne l'état à 'faux' !
L'état 'indéterminé' est absorbant pour l'opérateur '==' :
if(boost::logic::tribool(boost::logic::indeterminate)==boost::logic::tribool(boost::logic::indeterminate)) échoue !
L'état 'indéterminé' est absorbant pour l'opérateur '!=' :
if(boost::logic::tribool(boost::logic::indeterminate)!=boost::logic::tribool(true)) échoue !
if(boost::logic::tribool(boost::logic::indeterminate)!=boost::logic::tribool(false)) échoue !
if(boost::logic::tribool(boost::logic::indeterminate)!=boost::logic::tribool(boost::logic::indeterminate)) échoue !
VII. Comment ça marche ?▲
VII-A. Les fichiers▲
- boost/logic/tribool.hpp : définition de la classe boost::logic::tribool, du type boost::logic::indeterminate et des surcharges des opérateurs ;
- boost/logic/tribool_fwd.hpp : déclaration de la classe ;
- boost/logic/tribool_io.hpp : définition des surcharges nécessaires aux opérations d'entrée/sortie avec les flux.
Boost.Tribool se compose essentiellement de trois fichiers :
VII-B. La classe▲
Voici la définition de la classe :
class
tribool
{
private
:
///
INTERNAL ONLY
struct
dummy {
void
nonnull() {}
;
}
;
typedef
void
(dummy::
*
safe_bool)();
public
:
tribool() : value(false_value) {}
tribool(bool
value) : value(value? true_value : false_value) {}
tribool(indeterminate_keyword_t) : value(indeterminate_value) {}
operator
safe_bool() const
{
return
value ==
true_value? &
dummy::
nonnull : 0
;
}
enum
value_t {
false_value, true_value, indeterminate_value }
value;
}
;
Rien de bien complexe dans cette classe. La seule subtilité est la définition de safe_bool. Cela permet de le rendre interne à la classe et privé en évitant ainsi d'éventuels conflits ou de mésusage.
VII-C. Le type 'indéterminé'▲
Une structure interne est d'abord déclarée :
struct
indeterminate_t
{
#if BOOST_WORKAROUND(__BORLANDC__, < 0x0600)
char
dummy_; // BCB would use 8 bytes by default
#endif
}
;
Elle n'a pas pour but d'être utilisée ! Elle sert à définir le type d'un mot-clé pour l'état 'indéterminé' :
typedef
bool
(*
indeterminate_keyword_t)(tribool, detail::
indeterminate_t);
C'est un pointeur de fonction pour pouvoir utiliser, comme nous l'avons vu, le mot-clé aussi bien comme valeur que comme fonction :
tribool ma_variable(indeterminate);//utilisation en valeur
if
(indeterminate(ma_variable){
// utilisation en fonction
...
L'utilisation du type interne detail::indeterminate_t permet d'éviter la définition par l'utilisateur d'une fonction qui aurait la même signature.
Le mot-clé par défaut 'indeterminate' est tout simplement une fonction suivant la définition précédente :
inline
bool
indeterminate(tribool x,
detail::
indeterminate_t dummy =
detail::
indeterminate_t());
inline
bool
indeterminate(tribool x, detail::
indeterminate_t)
{
return
x.value ==
tribool::
indeterminate_value;
}
Enfin, la macro suivante définit un mot-clé utilisateur en déclarant une fonction du nom voulu suivant la signature indeterminate_keyword_t :
#define BOOST_TRIBOOL_THIRD_STATE(Name) \
inline bool \
Name(boost::logic::tribool x, \
boost::logic::detail::indeterminate_t dummy = \
boost::logic::detail::indeterminate_t()) \
{ return x.value == boost::logic::tribool::indeterminate_value; }
VII-D. Les opérateurs▲
La surcharge des opérateurs est assez simple et ne présente aucune particularité :
inline
tribool operator
!
(tribool x)
{
return
x.value ==
tribool::
false_value? tribool(true
)
:
x.value ==
tribool::
true_value? tribool(false
)
:
tribool(indeterminate);
}
inline
tribool operator
&&
(tribool x, tribool y)
{
if
(static_cast
<
bool
>
(!
x) ||
static_cast
<
bool
>
(!
y))
return
false
;
else
if
(static_cast
<
bool
>
(x) &&
static_cast
<
bool
>
(y))
return
true
;
else
return
indeterminate;
}
inline
tribool operator
&&
(tribool x, bool
y)
{
return
y? x : tribool(false
); }
inline
tribool operator
&&
(bool
x, tribool y)
{
return
x? y : tribool(false
); }
inline
tribool operator
&&
(indeterminate_keyword_t, tribool x)
{
return
!
x? tribool(false
) : tribool(indeterminate); }
inline
tribool operator
&&
(tribool x, indeterminate_keyword_t)
{
return
!
x? tribool(false
) : tribool(indeterminate); }
inline
tribool operator
||
(tribool x, tribool y)
{
if
(static_cast
<
bool
>
(!
x) &&
static_cast
<
bool
>
(!
y))
return
false
;
else
if
(static_cast
<
bool
>
(x) ||
static_cast
<
bool
>
(y))
return
true
;
else
return
indeterminate;
}
inline
tribool operator
||
(tribool x, bool
y)
{
return
y? tribool(true
) : x; }
inline
tribool operator
||
(bool
x, tribool y)
{
return
x? tribool(true
) : y; }
inline
tribool operator
||
(indeterminate_keyword_t, tribool x)
{
return
x? tribool(true
) : tribool(indeterminate); }
inline
tribool operator
||
(tribool x, indeterminate_keyword_t)
{
return
x? tribool(true
) : tribool(indeterminate); }
inline
tribool operator
==
(tribool x, tribool y)
{
if
(indeterminate(x) ||
indeterminate(y))
return
indeterminate;
else
return
(x &&
y) ||
(!
x &&
!
y);
}
inline
tribool operator
==
(tribool x, bool
y) {
return
x ==
tribool(y); }
inline
tribool operator
==
(bool
x, tribool y) {
return
tribool(x) ==
y; }
inline
tribool operator
==
(indeterminate_keyword_t, tribool x)
{
return
tribool(indeterminate) ==
x; }
inline
tribool operator
==
(tribool x, indeterminate_keyword_t)
{
return
tribool(indeterminate) ==
x; }
inline
tribool operator
!=
(tribool x, tribool y)
{
if
(indeterminate(x) ||
indeterminate(y))
return
indeterminate;
else
return
!
((x &&
y) ||
(!
x &&
!
y));
}
inline
tribool operator
!=
(tribool x, bool
y) {
return
x !=
tribool(y); }
inline
tribool operator
!=
(bool
x, tribool y) {
return
tribool(x) !=
y; }
inline
tribool operator
!=
(indeterminate_keyword_t, tribool x)
{
return
tribool(indeterminate) !=
x; }
inline
tribool operator
!=
(tribool x, indeterminate_keyword_t)
{
return
x !=
tribool(indeterminate); }
VII-E. Les flux▲
VII-E-1. Les valeurs littérales▲
Si la STL n'implémente pas locale, alors la bibliothèque doit être utilisée avec la directive de compilation BOOST_NO_STD_LOCALE. Les fonctions suivantes sont alors définies pour obtenir les valeurs littérales :
#ifdef BOOST_NO_STD_LOCALE
template
<
typename
T>
std::
basic_string<
T>
default_false_name();
template
<>
inline
std::
basic_string<
char
>
default_false_name<
char
>
()
{
return
"false"
; }
# ifndef BOOST_NO_WCHAR_T
template
<>
inline
std::
basic_string<
wchar_t
>
default_false_name<
wchar_t
>
()
{
return
L"false"
; }
# endif
template
<
typename
T>
std::
basic_string<
T>
default_true_name();
template
<>
inline
std::
basic_string<
char
>
default_true_name<
char
>
()
{
return
"true"
; }
# ifndef BOOST_NO_WCHAR_T
template
<>
inline
std::
basic_string<
wchar_t
>
default_true_name<
wchar_t
>
()
{
return
L"true"
; }
#endif
Par défaut, les fonctions sont spécialisées sur char et wchar_t.
Concernant l'état 'indéterminé', la valeur littérale doit, en tout état de cause, être produite car la facette spécifique peut ne pas avoir été positionnée dans le flux. Les fonctions suivantes sont donc systématiquement définies (indépendamment de la directive BOOST_NO_STD_LOCALE) :
template
<
typename
T>
std::
basic_string<
T>
get_default_indeterminate_name();
template
<>
inline
std::
basic_string<
char
>
get_default_indeterminate_name<
char
>
()
{
return
"indeterminate"
; }
#if BOOST_WORKAROUND(BOOST_MSVC, < 1300)
// VC++ 6.0 chokes on the specialization below, so we're stuck without
// wchar_t support. What a pain. TODO: it might just need a the template
// parameter as function parameter...
#else
# ifndef BOOST_NO_WCHAR_T
template
<>
inline
std::
basic_string<
wchar_t
>
get_default_indeterminate_name<
wchar_t
>
()
{
return
L"indeterminate"
; }
# endif
#endif
Les fonctions sont spécialisées par défaut pour char et wchar_t, sous réserve d'un compilateur différent de Visual C++ 6.0.
VII-E-2. La facette▲
La classe suivante définit la facette utilisée ensuite dans les opérateurs d'entrée et de sortie :
template
<
typename
CharT>
class
indeterminate_name : public
std::locale::
facet, private
boost::
noncopyable
{
public
:
typedef
CharT char_type;
typedef
std::
basic_string<
CharT>
string_type;
///
Construct the facet with the default name
indeterminate_name() : name_(get_default_indeterminate_name<
CharT>
()) {}
///
Construct the facet with the given name for the indeterminate value
explicit
indeterminate_name(const
string_type&
name) : name_(name) {}
///
Returns the name for the indeterminate value
string_type name() const
{
return
name_; }
///
Uniquily identifies this facet with the locale.
static
std::locale::
id id;
private
:
string_type name_;
}
;
template
<
typename
CharT>
std::locale::
id indeterminate_name<
CharT>
::
id;
La classe dérive de std::locale::facet, comme il se doit pour définir sa propre facette. Des informations sur ces aspects sont disponibles dans le tutoriel de developpez.com suivant : Tutoriel locale.
Elle dérive aussi de boost::noncopyable. Son nom assez explicite reflète l'objectif de cette classe : ne pas permettre à une classe qui en dérive d'être recopiée. Le constructeur par copie et l'opérateur d'affectation sont rendus privés. Cette classe fait partie de la bibliothèque Boost.Utility. Des informations supplémentaires sont disponibles sur le site de Boost.Class noncopyable.
L'attribut name_ contient la valeur littérale effective de la facette. Avec le constructeur par défaut, la valeur est récupérée avec la fonction get_default_indeterminate_name vu ci-dessus. Le second constructeur permet de positionner sa propre valeur littérale.
L'accesseur string_type name() const permet de récupérer cet attribut.
L'attribut id concerne la gestion de la facette par locale.
VII-E-3. L'opérateur de sortie▲
L'opérateur '<<' est surchargé comme suit :
template
<
typename
CharT, typename
Traits>
inline
std::
basic_ostream<
CharT, Traits>&
operator
<<
(std::
basic_ostream<
CharT, Traits>&
out, tribool x)
{
if
(!
indeterminate(x)) {
out <<
static_cast
<
bool
>
(x);
}
else
{
typename
std::
basic_ostream<
CharT, Traits>
::
sentry cerberus(out);
if
(cerberus) {
if
(out.flags() &
std::ios_base::
boolalpha) {
#ifndef BOOST_NO_STD_LOCALE
if
(BOOST_HAS_FACET(indeterminate_name<
CharT>
, out.getloc())) {
const
indeterminate_name<
CharT>&
facet =
BOOST_USE_FACET(indeterminate_name<
CharT>
, out.getloc());
out <<
facet.name();
}
else
{
out <<
get_default_indeterminate_name<
CharT>
();
}
#else
out <<
get_default_indeterminate_name<
CharT>
();
#endif
}
else
out <<
2
;
}
}
return
out;
}
La première ligne de la fonction montre que, quelle que soit l'implémentation de la STL, la sortie pour un état 'vrai' ou 'faux' est strictement identique à celle d'un bool.
La classe std::basic_ostream<CharT, Traits>::sentry doit être utilisée systématiquement lorsque l'opérateur '<<' est surchargé. Ce point peut être approfondi dans le cours suivant de développez.com : les flux d'entrée/sortie
L'écriture du littéral pour une variable tribool dans l'état 'indéterminé' suit ce qui a été précédemment expliqué. Le littéral de la facette est récupérée dès lors que la STL implémente locale et que la facette existe dans le flux. Sinon, la fonction get_default_indeterminate_name retourne ce littéral.
L'opérateur est aussi surchargé pour la valeur 'indéterminé' :
template
<
typename
CharT, typename
Traits>
inline
std::
basic_ostream<
CharT, Traits>&
operator
<<
(std::
basic_ostream<
CharT, Traits>&
out,
bool
(*
)(tribool, detail::
indeterminate_t))
{
return
out <<
tribool(indeterminate); }
Une instance de tribool permet de ne pas avoir à recopier de code…
VII-E-4. L'opérateur d'entrée▲
L'opérateur '>>' est surchargé comme suit :
template
<
typename
CharT, typename
Traits>
inline
std::
basic_istream<
CharT, Traits>&
operator
>>
(std::
basic_istream<
CharT, Traits>&
in, tribool&
x)
{
if
(in.flags() &
std::ios_base::
boolalpha) {
typename
std::
basic_istream<
CharT, Traits>
::
sentry cerberus(in);
if
(cerberus) {
typedef
std::
basic_string<
CharT>
string_type;
#ifndef BOOST_NO_STD_LOCALE
const
std::
numpunct<
CharT>&
numpunct_facet =
BOOST_USE_FACET(std::
numpunct<
CharT>
, in.getloc());
string_type falsename =
numpunct_facet.falsename();
string_type truename =
numpunct_facet.truename();
string_type othername;
if
(BOOST_HAS_FACET(indeterminate_name<
CharT>
, in.getloc())) {
othername =
BOOST_USE_FACET(indeterminate_name<
CharT>
, in.getloc()).name();
}
else
{
othername =
get_default_indeterminate_name<
CharT>
();
}
#else
string_type falsename =
default_false_name<
CharT>
();
string_type truename =
default_true_name<
CharT>
();
string_type othername =
get_default_indeterminate_name<
CharT>
();
#endif
typename
string_type::
size_type pos =
0
;
bool
falsename_ok =
true
, truename_ok =
true
, othername_ok =
true
;
// Modeled after the code from Library DR 17
while
(falsename_ok &&
pos <
falsename.size()
||
truename_ok &&
pos <
truename.size()
||
othername_ok &&
pos <
othername.size()) {
typename
Traits::
int_type c =
in.get();
if
(c ==
Traits::
eof())
return
in;
bool
matched =
false
;
if
(falsename_ok &&
pos <
falsename.size()) {
if
(Traits::
eq(Traits::
to_char_type(c), falsename[pos]))
matched =
true
;
else
falsename_ok =
false
;
}
if
(truename_ok &&
pos <
truename.size()) {
if
(Traits::
eq(Traits::
to_char_type(c), truename[pos]))
matched =
true
;
else
truename_ok =
false
;
}
if
(othername_ok &&
pos <
othername.size()) {
if
(Traits::
eq(Traits::
to_char_type(c), othername[pos]))
matched =
true
;
else
othername_ok =
false
;
}
if
(matched) {
++
pos; }
if
(pos >
falsename.size()) falsename_ok =
false
;
if
(pos >
truename.size()) truename_ok =
false
;
if
(pos >
othername.size()) othername_ok =
false
;
}
if
(pos ==
0
)
in.setstate(std::ios_base::
failbit);
else
{
if
(falsename_ok) x =
false
;
else
if
(truename_ok) x =
true
;
else
if
(othername_ok) x =
indeterminate;
else
in.setstate(std::ios_base::
failbit);
}
}
}
else
{
long
value;
if
(in >>
value) {
switch
(value) {
case
0
: x =
false
; break
;
case
1
: x =
true
; break
;
case
2
: x =
indeterminate; break
;
default
:
in.setstate(std::ios_base::
failbit); break
;
}
}
}
return
in;
}
Commençons par le plus simple : le drapeau std::ios_base::boolalpha n'est pas levé :
long
value;
if
(in >>
value) {
switch
(value) {
case
0
: x =
false
; break
;
case
1
: x =
true
; break
;
case
2
: x =
indeterminate; break
;
default
:
in.setstate(std::ios_base::
failbit); break
;
}
}
Le paramètre tribool prend la valeur 'faux' pour une valeur numérique de 0, 'vrai' pour 1 et 'indéterminé' pour 2. Si l'entier lu est d'une valeur différente de ces dernières, le drapeau std::ios_base::failbit est levé.
Lorsque la lecture se fait à partir du littéral, le drapeau std::ios_base::boolalpha est levé. La fonction commence par récupérer les valeurs à reconnaître. Lorsque la STL n'implémente pas locale, les fonctions sont utilisées pour récupérer les chaînes :
string_type falsename =
default_false_name<
CharT>
();
string_type truename =
default_true_name<
CharT>
();
string_type othername =
get_default_indeterminate_name<
CharT>
();
Lorsque la STL implémente locale, les littéraux pour l'état 'vrai' et 'faux' sont repris de la facette les définissant pour le type bool :
const
std::
numpunct<
CharT>&
numpunct_facet =
BOOST_USE_FACET(std::
numpunct<
CharT>
, in.getloc());
string_type falsename =
numpunct_facet.falsename();
string_type truename =
numpunct_facet.truename();
Le littéral pour l'état 'indéterminé' est récupéré par la facette indeterminate_name si elle est définie, sinon par get_default_indeterminate_name :
string_type othername;
if
(BOOST_HAS_FACET(indeterminate_name<
CharT>
, in.getloc())) {
othername =
BOOST_USE_FACET(indeterminate_name<
CharT>
, in.getloc()).name();
}
else
{
othername =
get_default_indeterminate_name<
CharT>
();
}
Ensuite, il s'agit d'un simple automate de reconnaissance des trois valeurs possibles :
typename
string_type::
size_type pos =
0
;
bool
falsename_ok =
true
, truename_ok =
true
, othername_ok =
true
;
// Modeled after the code from Library DR 17
while
(falsename_ok &&
pos <
falsename.size()
||
truename_ok &&
pos <
truename.size()
||
othername_ok &&
pos <
othername.size()) {
typename
Traits::
int_type c =
in.get();
if
(c ==
Traits::
eof())
return
in;
bool
matched =
false
;
if
(falsename_ok &&
pos <
falsename.size()) {
if
(Traits::
eq(Traits::
to_char_type(c), falsename[pos]))
matched =
true
;
else
falsename_ok =
false
;
}
if
(truename_ok &&
pos <
truename.size()) {
if
(Traits::
eq(Traits::
to_char_type(c), truename[pos]))
matched =
true
;
else
truename_ok =
false
;
}
if
(othername_ok &&
pos <
othername.size()) {
if
(Traits::
eq(Traits::
to_char_type(c), othername[pos]))
matched =
true
;
else
othername_ok =
false
;
}
if
(matched) {
++
pos; }
if
(pos >
falsename.size()) falsename_ok =
false
;
if
(pos >
truename.size()) truename_ok =
false
;
if
(pos >
othername.size()) othername_ok =
false
;
}
Finalement, le paramètre tribool est renseigné si la valeur a été correctement lue, sinon le std::ios_base::failbit drapeau est levé :
if
(pos ==
0
)
in.setstate(std::ios_base::
failbit);
else
{
if
(falsename_ok) x =
false
;
else
if
(truename_ok) x =
true
;
else
if
(othername_ok) x =
indeterminate;
else
in.setstate(std::ios_base::
failbit);
}
VIII. Soyons logiques !▲
Essayons de comprendre les choix opérés sur les définitions des différents opérateurs: &&, ||, !, == et !=.
VIII-A. Rappel sur l'algèbre de Boole▲
Pour ne pas effrayer les mathématiciens, disons tout de suite, que dans le cadre de ce tutoriel, l'algèbre de Boole s'entend plus sur le calcul booléen tel qu'il peut être présenté dans les cours d'informatique ou d'électronique. Il ne part pas des structures algébriques telles que définies en mathématiques ce qui serait plus exact et plus complet, mais risquerait par la technicité de l'exposé de masquer l'objectif premier de cette partie : expliquer les choix opérés pour construire les opérateurs.
On part de l'ensemble B = {V,F}.
Par définition, on construit les 3 fonctions suivantes :
-
ET : BxB->B
V
F
V
V
F
F
F
F
Remarques :
F est un élément absorbant pour ET : Quelque soit x de B, ET(x,F) = F.
V est un élément neutre pour ET : Quelque soit x de B, ET(x,V) = x.
ET est commutatif : ET(x,y) = ET(y,x).
ET est idempotente : ET(x,x) = x. -
OU : BxB->B
V
F
V
V
V
F
V
F
Remarques :
V est un élément absorbant pour OU : Quelque soit x de B, OU(x,V) = V.
F est un élément neutre pour OU : Quelque soit x de B, OU(x,F) = x.
OU est commutatif : OU(x,y) = OU(y,x).
OU est idempotente : OU(x,x) = x. - NON : B->B
NON(V) = F
NON(F) = V
Remarque : NON(NON(x)) = x.
L'opérateur == se construit par définition à partir des trois fonctions précédentes :
x==y : BxB->B
x==y = ((x ET y) OU (NON(x) ET NON(y)))
De la même façon, on construit l'opérateur != par définition :
x!=y : BxB->B
x==y = NON(x==y) = NON(((x ET y) OU (NON(x) ET NON(y)))) = (x OU y) ET NON(x ET y).
Le lecteur avisé reconnaît ici la définition habituelle de XOR.
En appliquant les tables de vérités, on s'aperçoit que nos définitions ont bien le sens commun qu'on attribue aux opérateurs.
VIII-B. L'algèbre tri-booléenne▲
On définit notre ensemble de départ T = {V,F,I}
Pour définir nos tables de vérités, on va chercher à maintenir la cohérence des propriétés citées ci-dessus. On va donc reprendre les différentes propriétés des lois de composition pour remplir nos trous.
VIII-B-1. L'opérateur ET▲
Pour commencer, remplissions notre table de vérité pour l'opérateur ET avec celle présentée ci-dessus pour les éléments V et F :
V |
F |
I |
|
V |
V |
F |
|
F |
F |
F |
|
I |
Pour remplir les éléments manquants, on part des remarques présentées pour l'opérateur ET :
- ET est commutatif : ET(I,y) = ET(y,I)
- F est un élément absorbant pour ET : ET(F,I) = ET(I,F) = F
- V est un élément neutre pour ET : ET(V,I) = ET(I,V) = I
- ET est idempotente : ET(I,I) = I.
On obtient :
V |
F |
I |
|
V |
V |
F |
I |
F |
F |
F |
F |
I |
I |
F |
I |
Voilà notre définition de l'opérateur && pleinement spécifiée.
VIII-B-2. L'opérateur OU▲
On opère de la même façon pour OU en partant de la table de vérité des booléens :
V |
F |
I |
|
V |
V |
V |
|
F |
V |
F |
|
I |
Pour remplir les éléments manquants, on part des remarques présentées pour l'opérateur OU :
- OU est commutatif : OU(I,y) = OU(y,I)
- V est un élément absorbant pour OU : OU(V,I) = OU(I,V) = V
- F est un élément neutre pour OU : OU(F,I) = OU(I,F) = I
- OU est idempotente : OU(I,I) = I.
On obtient :
V |
F |
I |
|
V |
V |
V |
V |
F |
V |
F |
I |
I |
V |
I |
I |
Grace à cette table, l'opérateur || est totalement spécifié.
VIII-B-3. L'opérateur NON▲
Partons de la propriété NON(NON(x))=x et raisonnons par l'absurde
- Soit NON(I) = V
Il s'en suit que NON(NON(I)) = NON(V) = F !
On ne respecte pas notre propriété NON(NON(x))=x. - Soit NON(I) = F
Il s'en suit que NON(NON(I)) = NON(F) = V !
On ne respecte pas notre propriété NON(NON(x))=x. - Soit NON(I) = I
Il s'en suit que NON(NON(I)) = NON(I) = I !
On respecte notre propriété NON(NON(x))=x.
Notre définition de NON devient :
- NON : B->B
- NON(V) = F
- NON(F) = V
- NON(I) = I
L'opérateur ! est maintenant complètement spécifié.
VIII-B-4. L'opérateur ==▲
On reprend la définition donnée pour le calcul booléen de l'opérateur == :
x==y : BxB->B
x==y = ((x ET y) OU (NON(x) ET NON(y)))
On applique les tables de vérités ci-dessus et on obtient :
V |
F |
I |
|
V |
V |
F |
I |
F |
F |
V |
I |
I |
I |
I |
I |
Cette table de vérité explique le choix de la surcharge de l'opérateur == pour la classe Boost.Tribool. On comprend maintenant pourquoi boost::logic::tribool(indeterminate) == boost::logic::tribool(indeterminate) vaut indeterminate, ce qui nous avait surpris en début de ce tutoriel !
VIII-B-5. L'opérateur !=▲
Comme précédemment, on repart de la définition de != (NON(==)) et on applique les tables de vérités
V |
F |
I |
|
V |
F |
V |
I |
F |
V |
F |
I |
I |
I |
I |
I |