Outils d'utilisateurs

Outils du Site


bitset

Différences

Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.

Lien vers cette vue

bitset [2016/04/18 20:20]
gbdivers
bitset [2016/07/05 18:54] (Version actuelle)
gbdivers
Ligne 1: Ligne 1:
  
-^ [[predicats|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[valarray|Chapitre suivant]] ^+^ [[predicats|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[string_etendu|Chapitre suivant]] ^
  
-====== Les tableaux de bits ======+__ A rediger: cast vers d'autres types, bit shift__
  
-Vous avez déjà rencontrer la classe ''std::bitset'' dans le chapitre [[logique_et_calcul_booleen|]], pour afficher une séquence de bits. +====== [Aller plus loin] Les tableaux de bits ====== 
 + 
 +Vous avez déjà rencontré la classe ''std::bitset'' dans le chapitre [[logique_et_calcul_booleen|]], pour afficher une séquence de bits. 
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 25: Ligne 27:
 </code> </code>
  
-Ce chapitre détaille l'utilisation de cette classe ''std::bitset'' et les notions de //flag// et //mask//. Les notions vues dans les chapitres [[logique_et_calcul_booleen|]] et [[boole_et_morgan|]] seront utilisees, n'hesitez pas les relire si nécessaire.+Ce chapitre détaille l'utilisation de cette classe ''std::bitset'' et les notions de //flag// et //mask//. Les notions vues dans les chapitres [[logique_et_calcul_booleen|]] et [[boole_et_morgan|]] seront utilisées, n’hésitez pas à les relire si nécessaire.
  
  
 ===== Créer un std::bitset ===== ===== Créer un std::bitset =====
  
-La classe ''std::bitset'' est une classe template prenant en argument le nombre de bits . La taille d'un ''std::bitset'' est donc déterminée à la compilation (Pour rappel, vous avez deja rencontré des classes qui ont des tailles determinées a la compilation ou l'exécution respectivement ''std::array'' et ''std::vector'').+La classe ''std::bitset'' est une classe //template// prenant en argument le nombre de bits. La taille d'un ''std::bitset'' est donc déterminée à la compilation (Pour rappel, vous avez déjà rencontré une classe qui a une taille déterminée à la compilation : ''std::array'').
  
-Le nombre de bits geres par ''std::bitset'' est un argument template (donc qui s'écrit entre ''<>'') de type entier.+Le nombre de bits gérés par ''std::bitset'' est un argument //template// (donc qui s'écrit entre chevrons ''<>'') de type entier.
  
 <code cpp> <code cpp>
Ligne 38: Ligne 40:
 </code> </code>
  
-Il existe differentes methodes pour initialiser un ''std::bitset'' :+Il existe différentes méthodes pour initialiser un ''std::bitset'' :
  
-  * avec une valeur entiere ;+  * avec une valeur entière ;
   * avec une chaîne de caractères.   * avec une chaîne de caractères.
  
Ligne 49: Ligne 51:
    
 int main() { int main() {
-    const std::bitset<8> b;+    const std::bitset<8> b {};
     const std::bitset<8> b8 { 0b101010 };     const std::bitset<8> b8 { 0b101010 };
     const std::bitset<16> b16 { 0xA1B2 };     const std::bitset<16> b16 { 0xA1B2 };
Ligne 55: Ligne 57:
 </code> </code>
  
-Pour faciliter la création de ''std::bitset'' à partir d'une entrée utilisateur (flux standard, fichier, etc), il est également possible d'initialiser un ''std::bitset'' à partir d'une chaîne de caractères. La syntaxe peut etre differente selon si vous utiliser une litterale chaine ou un type ''std::string''.+Pour faciliter la création de ''std::bitset'' à partir d'une entrée utilisateur (flux standard, fichier, etc), il est également possible d'initialiser un ''std::bitset'' à partir d'une chaîne de caractères. La syntaxe peut etre différente selon si vous utiliser une littérale chaîne ou un type ''std::string''.
  
-Le cas le plus simple est d'initialiser un ''std::bitset'' partir d'une chaine completeconstituee des caracteres ''0'' et ''1''. Dans ce cas, la syntaxe est identique pour une litterale et un ''std::string'' :+Le cas le plus simple est d'initialiser un ''std::bitset'' à partir d'une chaîne complèteconstituée des caractères ''0'' et ''1''. Dans ce cas, la syntaxe est identique pour une littérale et un ''std::string'' :
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 71: Ligne 73:
 </code> </code>
  
-Notez bien qu'il ne faut pas ajouter de prefixe dans la chaine de caracteres.+Notez bien qu'il ne faut pas ajouter de préfixe dans la chaîne de caractères.
  
-Un ''std::bitset'' peut egalement etre initialise a partir d'une sous-chaine de caracteres (c'est dire une partie d'une chaine de caracteres). La syntaxe est differentes entre une litterale et un ''st::string'' :+Un ''std::bitset'' peut également être initialisé à partir d'une sous-chaîne de caractères (c'est-à-dire une partie d'une chaîne de caractères). La syntaxe est différentes entre une littérale et un ''std::string'' :
  
-  * pour une litterale, l'initialisation de ''std::bitset'' ne peut prendre qu'un seul argument optionnel supplementaire : le nombre de caracteres a conserver ; +  * pour une littérale, l'initialisation de ''std::bitset'' ne peut prendre qu'un seul argument optionnel supplémentaire : le nombre de caractères a conserver ; 
-  * pour un ''std::string'', ''std::bitset'' peut prendre deux arguments optionnels supplementaires : la position du premier caractere et le nombre de caracteres a conserver.+  * pour un ''std::string'', ''std::bitset'' peut prendre deux arguments optionnels supplémentaires : la position du premier caractère et le nombre de caractères à conserver.
  
-Un exemple concret avec une litterale chaine :+Un exemple concret avec une littérale chaîne :
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 85: Ligne 87:
    
 int main() { int main() {
-    const std::bitset<8> b1 { "1010101011", 4};  // les 4 premiers caracteres = "1010"+    const std::bitset<8> b1 { "1010101011", 4};  // les 4 premiers caractères = "1010"
     std::cout << b1 << std::endl;     std::cout << b1 << std::endl;
          
-    const std::bitset<8> b2 { "1010101011", 8};  // les 8 premiers caracteres = "10101010"+    const std::bitset<8> b2 { "1010101011", 8};  // les 8 premiers caractères = "10101010"
     std::cout << b2 << std::endl;     std::cout << b2 << std::endl;
 } }
Ligne 110: Ligne 112:
     const std::string s { "1010101011" };     const std::string s { "1010101011" };
          
-    const std::bitset<8> b1 { s, 4 };    // commence l'indice 4 = "101011"+    const std::bitset<8> b1 { s, 4 };    // commence à l'indice 4 = "101011"
     std::cout << b1 << std::endl;     std::cout << b1 << std::endl;
          
-    const std::bitset<8> b2 { s, 4, 2 }; // commence l'indice 4 et conserve +    const std::bitset<8> b2 { s, 4, 2 }; // commence à l'indice 4 et conserve 
-                                         // 2 caracteres = "10"+                                         // 2 caractères = "10"
     std::cout << b2 << std::endl;     std::cout << b2 << std::endl;
 } }
Ligne 126: Ligne 128:
 </code> </code>
  
-N'oubliez pas qu'en C++, les indices dans les tableaux (et donc dans les chaines de caracteres, puisqu'elles peuvent etre considerees comme des tableaux de caracteres) commencent l'indice 0. Donc l'indice 4 correspond au cinquieme caractere :+N'oubliez pas qu'en C++, les indices dans les tableaux (et donc dans les chaines de caractères, puisqu'elles peuvent être considérées comme des tableaux de caractères) commencent à l'indice 0. Donc l'indice 4 correspond au cinquième caractère :
  
 <code> <code>
Ligne 135: Ligne 137:
 </code> </code>
  
-Pour terminer, il est possible d'utiliser d'autres caracteres que ''0'' et ''1''. Pour cela, il faut fournir deux arguments supplementaires, correspondent respectivement aux caracteres a utiliser la place de ''0'' et de ''1''.+Pour terminer, il est possible d'utiliser d'autres caractères que ''0'' et ''1''. Pour cela, il faut fournir deux arguments supplémentaires, correspondent respectivement aux caractères à utiliser à la place de ''0'' et de ''1''.
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 159: Ligne 161:
 </code> </code>
  
-Notez que les caracteres utilisees pour representer le ''std::bitset'' ne sont utilises que pour l'initialisation. En memoire et lors de l'affichage, un ''std::bitset'' sera representes par defaut par une suite de ''0'' et ''1''.+Notez que les caractères utilisés pour représenter le ''std::bitset'' ne le sont que pour l'initialisation. La représentation en mémoire sera toujours identique et l'affichage utilisera par défaut les caractères ''0'' et ''1''.
  
 <note>**Ordres des arguments et arguments optionnels** <note>**Ordres des arguments et arguments optionnels**
  
-En C++, les arguments sont identifies par leur position dans l'appel d'une fonction. Par exemple pour initialiser ''std::bitset'' (avec ''position'' qui represente la position du premier caractere et ''taille'' qui correspond au nombre de caracteres a conserver : +En C++, les arguments sont identifiés par leur position dans l'appel d'une fonction. Par exemple pour initialiser ''std::bitset'' (avec ''position'' qui représente la position du premier caractère et ''taille'' qui correspond au nombre de caractères à conserver : 
  
 <code> <code>
Ligne 177: Ligne 179:
 </code> </code>
  
-Dans ce code, le compilateur ne va pas utiliser ''taille'' pour le nombre de caracteres et ''position'' pour la position du premier caractere (donc prendre 4 caracteres a partir de la position 2), mais va prendre 2 caracteres a partir de la position 4.+Dans ce code, le compilateur ne va pas utiliser ''taille'' pour le nombre de caractères et ''position'' pour la position du premier caractère (donc prendre 4 caractères à partir de la position 2), mais va prendre 2 caractères à partir de la position 4.
  
-Pour la meme raison, si on fournit un argument optionnel, les arguments optionnels qui le precedent ne sont plus optionnels.+Pour la même raison, si on fournit un argument optionnel, les arguments optionnels qui le précédent ne sont plus optionnels.
  
 <code> <code>
Ligne 185: Ligne 187:
 </code> </code>
  
-Dans ce code, le compilateur ne va pas utiliser l'argument ''taille'' fournit et considerer que l'argument ''position'' prend sa valeur par defaut 0 (donc prendre 4 caracteres a partir de la position 0), mais va utiliser ''taille'' comme position (donc prendre tous les caracteres apres la position 4).+Dans ce code, le compilateur ne va pas utiliser l'argument ''taille'' fournit et considérer que l'argument ''position'' prend sa valeur par défaut 0 (donc prendre 4 caractères a partir de la position 0), mais va utiliser ''taille'' comme position (donc prendre tous les caractères après la position 4).
  
-Les arguments de fonction, en particulier l'ordre des arguments et les arguments optionnels seront vu en detail dans les chapitres sur la creation de fonctions.+Les arguments de fonction, en particulier l'ordre des arguments et les arguments optionnels seront vu en détail dans les chapitres sur la création de fonctions.
 </note> </note>
  
 +Notez qu'il est possible d'affecter une valeur entière à un ''std:bitset'' en utilisant l'opérateur ''='', mais ce n'est pas possible directement avec une chaîne de caractères.
  
-===== Afficher et convertir en chaine de caracteres =====+<code> 
 +std::bitset<8> b {}; 
 +b = 0b110011;   // ok 
 +b = "110011";   // erreur 
 +</code> 
 + 
 +<note>**Construction explicite et implicite** 
 + 
 +En fait, la classe ''std::bitset'' ne propose pas d'opérateur d'affectation ''='' pour utiliser une valeur entière. Pourquoi est-il possible dans ce cas de pouvoir utiliser ''='' ? 
 + 
 +La classe ''std::bitset'' est copiable, ce qui signifie qu'il est possible d'affecter un ''std::bitset'' à un autre ''std::bitset''. 
 + 
 +<code> 
 +std::bitset<8> b1 {}; 
 +std::bitset<8> b2 {}; 
 +b2 = b1;  // ok 
 +</code> 
 + 
 +Dans le cas d'une valeur entière, le compilateur est autorisé à créer automatiquement et de façon transparente un ''std::bitset'' à partir de cette valeur. C'est-à-dire à remplacer : 
 + 
 +<code> 
 +b = 0b110011;   // ok 
 +</code> 
 + 
 +Par : 
 + 
 +<code> 
 +b = std::bitset { 0b110011 };   // ok 
 +</code> 
 + 
 +Le compilateur réalise une conversion //implicite//. 
 + 
 +Dans le cas des chaînes de caractères, cette conversion implicite est interdite, le compilateur ne peut réaliser que des conversions //explicites// (c'est à dire que le développeur doit explicitement écrire cette conversion). 
 + 
 +<code> 
 +std::bitset<8> b; 
 +b = 0b110011;                    // ok, conversion implicite 
 +b = std::bitset<8> { "110011" }; // ok, conversion explicite 
 +</code> 
 + 
 +Les conversion explicites et implicites seront détaillées dans la partie sur la création de classes. 
 +</note> 
 + 
 + 
 +===== Afficher et convertir en chaîne de caractères =====
  
-Comme vous l'avez vu dans les codes precedents, un ''std::bitset'' peut etre affiche directement avec ''std::cout''.+Comme vous l'avez vu dans les codes précédents, un ''std::bitset'' peut être affiche directement avec ''std::cout''.
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 211: Ligne 258:
 </code> </code>
  
-Dans ce cas, ''std::bitset'' sera affiche en utilisant les caracteres ''0'' et ''1'', avec autant de caracteres que definie dans l'argument template de ''std::bitset'' (donc en completant avec ''0'' si necessaire).+Dans ce cas, ''std::bitset'' sera affiche en utilisant les caractères ''0'' et ''1'', avec autant de caractères que définie dans l'argument //template// de ''std::bitset'' (donc en complétant avec ''0'' si nécessaire).
  
-La fonction ''to_string'' permet de transformer un ''std::bitset'' en une chaine de caracteres, en utilisant par defaut les caracteres ''0'' et ''1'' (c'est l'operation inverse de l'initialisation avec une chaine). Cette fonction peut prendre deux arguments optionnels, correspondant aux caracteres a utiliser respectivement pour ''0'' et ''1''. +La fonction ''to_string'' permet de transformer un ''std::bitset'' en une chaîne de caractères, en utilisant par défaut les caractères ''0'' et ''1'' (c'est l’opération inverse de l'initialisation avec une chaîne). Cette fonction peut prendre deux arguments optionnels, correspondant aux caractères à utiliser respectivement pour ''0'' et ''1''. 
  
-La chaine de caracteres produite peut etre conservee dans une variable ou etre directement affichee avec ''std::cout''.+La chaîne de caractères produite peut être conservée dans une variable ou être directement affichée avec ''std::cout''.
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 240: Ligne 287:
 ===== Manipuler individuellement les bits ===== ===== Manipuler individuellement les bits =====
  
-Conceptuellement, ''std::bitset'' est un tableau compact de booleens. Il est donc possible de manipuler directement chaque bit comme une valeur booleenne, de la lire ou de la modifier directement dans ''std::bitset''.+Conceptuellement, ''std::bitset'' est un tableau compact de booléens. Il est donc possible de manipuler directement chaque bit comme une valeur booléenne, de la lire ou de la modifier directement dans ''std::bitset''.
  
-Pour connaitre la taille d'un ''std::bitset'' (c'est dire connaitre la valeur de l'argument template ''TAILLE'' utilise pour initialiser le ''std::bitset''), vous pouvez utiliser la fonction ''size''.+Pour connaitre la taille d'un ''std::bitset'' (c'est-à-dire connaitre la valeur de l'argument //template// ''TAILLE'' utilise pour initialiser le ''std::bitset''), vous pouvez utiliser la fonction ''size''.
  
-<note>Vous voyez ici l'interet d'avoir une interface coherente : beaucoup de classe de la bibliotheque standard representant un tableau proposent la fonction ''size'' pour connaitre la taille du tableau (''std::vector'', ''std::array'', ''std::string'', etc). Avoir un interface coherente simplifie la memorisation et evite les erreurs. Conservez cette idee en tete lorsque vous creerez vos propres interfaces.</note>+<note>Vous voyez ici l’intérêt d'avoir une interface cohérente : beaucoup de classe de la bibliothèque standard représentant un tableau proposent la fonction ''size'' pour connaitre la taille du tableau (''std::vector'', ''std::array'', ''std::string'', etc). Avoir un interface cohérente simplifie la mémorisation et évite les erreurs. Conservez cette idée en tête lorsque vous créerez vos propres interfaces.</note>
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 251: Ligne 298:
    
 int main() { int main() {
-    const std::bitset<8> b1{}; +    const std::bitset<8> b8 {}; 
-    std::cout << b1.size() << std::endl; +    std::cout << b8.size() << std::endl; 
-    const std::bitset<16> b2{}; +    const std::bitset<16> b16{}; 
-    std::cout << b2 << std::endl;+    std::cout << b16.size() << std::endl;
 } }
 </code> </code>
Ligne 265: Ligne 312:
 </code> </code>
  
-<note>La fonction ''test''+==== L’opérateur d'indexation ====
  
-''std::bitset'' propose egalement la fonction ''test'' pour acceder en lecture a un bit specifiqueLa difference avec l'operateur ''[]'' est que la fonction ''test'' verifie si +Comme pour les autres tableaux de la bibliothèque standard (''std::array''''std::vector'', etc.), l'opérateur d'indexation ''[]'' permet d'accéder aux élément d'un ''std::bitset''. Comme toujours, l'indice doit avoir une valeur comprise entre 0 et ''SIZE-1''.
  
 +<code cpp main.cpp>
 +#include <iostream>
 +#include <bitset>
 +#include <cassert>
 + 
 +int main() {
 +    const std::bitset<8> b8 { 0b110111 };
 +    size_t index { 0 };
 +    assert(index < b8.size());
 +    std::cout << b8[index] << std::endl;
 +    index = 6;
 +    assert(index < b8.size());
 +    std::cout << b8[index] << std::endl;
 +}
 +</code>
  
-===== Mask et flag =====+affiche :
  
 +<code>
 +1
 +0
 +</code>
  
 +Notez bien que le bit correspondant à l'indice 0 est celui le plus à droite dans la représentation binaire (le bit de poids faible), le second bit est le second en partant de la droite et ainsi de suite. 
  
-tester un bit : mask, flag, opérateur ET bit à bit, test() +<code cpp main.cpp> 
-forcer un bit OU bit a bit+#include <iostream> 
 +#include <bitset> 
 +  
 +int main() { 
 +    const std::bitset<8> b8 { 0b110111 }; 
 +    std::cout << b8[0] << ' ' << b8[1] << ' ' << b8[2] << ' ' << b8[3] << ' ' <<  
 +                 b8[4] << ' ' << b8[5] << ' ' << b8[6] << ' ' << b8[7] << std::endl;
  
-tester plusieurs bit : count, all, any, none (cf algo)+</code>
  
 +affiche :
  
-Plusieurs fois des données de même type. Accès avec un indice, partant de 0. Taille fixé à la compilation ou à l'exécution : bitset à la compilation (vector<boolà l'exécution).+<code> 
 +1 1 1 0 1 1 0 0 
 +</code>
  
 +L'opérateur d'indexation ''[]'' est également utilisé pour modifier la valeur d'un bit en particulier, utilisant simplement l'opérateur d'affectation ''='', comme vous le feriez avec n'importe quelle variable.
  
-Validation taille assert +<code cpp main.cpp> 
-connaitre la taille size+#include <iostream> 
 +#include <bitset> 
 +  
 +int main() { 
 +    std::bitset<8> b8 {}; 
 +    std::cout << b8 << std::endl; 
 +    b8[0] = true; 
 +    b8[5] = true; 
 +    std::cout << b8 << std::endl; 
 +
 +</code> 
 + 
 +affiche : 
 + 
 +<code> 
 +00000000 
 +00100001 
 +</code> 
 + 
 + 
 +==== Les fonctions test, set, reset et flip ==== 
 + 
 +En complément de l’opérateur d'indexation ''[]'', qui permet d’accéder aux bits d'un ''std::bitset'' en lecture ET en écriture, la classe ''std::bitset'' propose également plusieurs fonctions plus spécialisées, pour accéder aux bits en lecture OU en écriture. 
 + 
 +<note>Une seconde différence importante est que ces fonctions vérifient les accès en dehors des limites de ''std::bitset'' et lancer une exception de type ''std::out_of_range'' si vous essayez d'utiliser un index invalide.  
 + 
 +La gestion des erreurs et les exceptions seront vu dans la partie sur la création de bibliothèque, mais retenez que la vérification systématique des accès est assimilée à de la programmation défensive, ce qui n'est pas forcément une bonne pratique.</note> 
 + 
 +Les fonctions sont les suivantes : 
 + 
 +  * ''test'' pour lire une valeur ; 
 +  * ''set'' pour modifier une valeur (par défaut pour la mettre à ''true'') ; 
 +  * ''reset'' pour mettre une valeur à ''false'' ; 
 +  * ''flip'' pour inverser une valeur (c'est-à-dire que les bits valant ''true'' passent à ''false'' et vice-versa). 
 + 
 +Ces fonctions prennent comme argument l'indice du bit à modifier. les fonctions ''set'', ''reset'' et ''flip'' peuvent également être appelée sans argument, ce qui a pour effet de modifier tous les bits. Pour terminer, la fonction ''set'' peut prendre deux arguments : l'indice du bit à modifier et la valeur booléenne à définir. 
 + 
 +<code cpp main.cpp> 
 +#include <iostream> 
 +#include <bitset> 
 +  
 +int main() { 
 +    std::bitset<8> b8 {}; 
 +    std::cout << b8 << std::endl; 
 +    b8.set(3); 
 +    std::cout << b8 << std::endl; 
 +    b8.set(); 
 +    std::cout << b8 << std::endl; 
 +    b8.set(5, false); 
 +    std::cout << b8 << std::endl; 
 +    b8.reset(3); 
 +    std::cout << b8 << std::endl; 
 +    b8.reset(); 
 +    std::cout << b8 << std::endl; 
 +    b8.flip(3); 
 +    std::cout << b8 << std::endl; 
 +    b8.flip(); 
 +    std::cout << b8 << std::endl; 
 +    std::cout << b8.test(2) << std::endl; 
 +    std::cout << b8.test(3) << std::endl; 
 +
 +</code> 
 + 
 +affiche : 
 + 
 +<code> 
 +00000000 
 +00001000 
 +11111111 
 +11011111 
 +11010111 
 +00000000 
 +00001000 
 +11110111 
 +
 +
 +</code> 
 + 
 + 
 +===== Tester plusieurs bits ===== 
 + 
 +La classe ''std::bitset'' permet de tester plusieurs bits : 
 + 
 +  * ''all'' permet de tester si tous les bits ont la valeur ''true'' ; 
 +  * ''any'' permet de tester si au moins un bit à la valeur ''true'' ; 
 +  * ''none'' permet de tester si aucun bit n'a la valeur ''true'' ; 
 +  * ''count'' permet de compter le nombre de bits qui ont la valeur ''true''. 
 + 
 +<code cpp main.cpp> 
 +#include <iostream> 
 +#include <bitset> 
 +  
 +int main() { 
 +    const std::bitset<4> b1("0000"); 
 +    const std::bitset<4> b2("0101"); 
 +    const std::bitset<4> b3("1111"); 
 +  
 +    std::cout << "bitset\t" << "all\t" << "any\t" << "none\t" <<  
 +        "count" << std::endl; 
 +    std::cout << b1 << '\t' << b1.all() << '\t' << b1.any() << '\t' <<  
 +        b1.none() << '\t' << b1.count() << std::endl; 
 +    std::cout << b2 << '\t' << b2.all() << '\t' << b2.any() << '\t' <<  
 +        b2.none() << '\t' << b2.count() << std::endl; 
 +    std::cout << b3 << '\t' << b3.all() << '\t' << b3.any() << '\t' <<  
 +        b3.none() << '\t' << b3.count() << std::endl; 
 +
 +</code> 
 + 
 +affiche : 
 + 
 +<code> 
 +bitset all any none count 
 +0000 0 0 1 0 
 +0101 0 1 0 2 
 +1111 1 1 0 4 
 +</code> 
 + 
 + 
 +===== Masquage et drapeau ===== 
 + 
 +<note>Il est également possible d'utiliser les énumérations pour représenter des drapeaux.</note> 
 + 
 +==== Les drapeaux ==== 
 + 
 +Il arrive souvent de devoir représenter l'état d'un concept en utilisant un nombre important de valeurs booléennes. Par exemple, une fenêtre dans une interface graphique sera visible ou non, pourra être déplaçable ou non, pourra avoir une barre de titre ou non, etc. Un véhicule pourra avoir trois ou cinq portes, pourra avoir un coffre ou non, pourra avoir ses phares allumés ou non, etc. 
 + 
 +Une première possibilité pour représenter cela est de créer autant de variables booléennes que vous avez d'états à représenter. 
 + 
 +<code cpp> 
 +// représentation de l'état d'une fenêtre 
 +bool is_visible {}; 
 +bool is_movable {}; 
 +bool has_title {}; 
 +</code> 
 + 
 +<note>Il existe de nombreuses habitudes de programmation. Par exemple comme dans le code précédent, les préfixes "is" et "has" sont utilisés respectivement les états "est" ou "n'est pas" et les états "a" ou "n'a pas". Ainsi, ''is_visible'' signifie littéralement "est visible" et sera vraie quand la fenêtre est visible et fausse dans le cas contraire.</note> 
 + 
 +Cependant, vous avez vu qu'un booléen en mémoire n'est pas représenté par un bit, mais par un octet en général (soit 8 bits). Cela signifie donc une consommation mémoire inutilement plus importante. 
 + 
 +<code cpp main.cpp> 
 +#include <iostream> 
 +  
 +int main() { 
 +    std::cout << sizeof(bool) << " octet(s)" << std::endl; 
 +
 +</code> 
 + 
 +affiche : 
 + 
 +<code> 
 +1 octet(s) 
 +</code> 
 + 
 +De plus, chaque variable est indépendante. Si vous voulez réinitialiser une fenêtre par exemple, il faudra modifier chaque booléen un par un, il n'est pas possible de les remettre tous à faux en une instruction. 
 + 
 +Pour améliorer cela, vous l'aurez surement deviné, il est possible d'utiliser ''std::bitset'' pour représenter un ensemble d'états booléens de façon compact. Chaque variable booléenne est maintenant replacée par un bit en particulier dans le ''std::bitset''. 
 + 
 +Par exemple, pour la fenêtre, il est possible d'utiliser un ''std::bitset<3>'', dans lequel le premier bit représente ''is_visible'', le second représente ''is_movable'' et le dernier ''has_title''. Il est alors possible d'utiliser les syntaxes vues précédemment pour manipuler l'état de la fenêtre. 
 + 
 +<code cpp> 
 +using window_state = std::bitset<3>; 
 + 
 +const window_state ws1 { 0b001 };    // fenêtre visible, non déplaçable et sans barre de titre 
 +const window_state ws2 { 0b110 };    // fenêtre masquée, déplaçable et avec une barre de titre 
 + 
 +window_state ws3; 
 +ws3.flip(0);                         // inverse is_visible, si la fenêtre était visible,  
 +                                     // elle devient invisible et réciproquement. 
 +const bool is_visible = ws3.test(0); // teste si la fenêtre est visible ou non 
 +</code> 
 + 
 +<note> 
 +Vous voyez ici un effet indésirable des optimisation : le code perd en lisibilité. Sans la documentation, il est impossible de savoir que ''0b001'' représente une fenêtre visible, non déplaçable et sans barre de titre, alors que ''is_visible'', ''is_movable'' et ''has_title'' sont suffisamment compréhensibles. 
 + 
 +Et plus généralement, lorsque vous concevrez un code, vous devrez faire des compromis entre vos objectifs de qualité logicielle (lisibilité, évolutivité, fiabilité, etc) et vos contraintes (temps de développement, moyens humains, etc). 
 +</note> 
 + 
 + 
 +==== Le masquage ==== 
 + 
 +Le masquage est un concept générique en informatique, qui consiste à définir un sous-ensemble sur lequel sera appliqué une modification (vous pouvez par exemple retrouver ce concept dans les masques de calque dans un logiciel de dessin tel que Gimp). 
 + 
 +Pour prendre un exemple concret, imaginez que vous avez la valeur ''0b11001110'' et que vous souhaitez modifier que les quatre premiers bits (en partant de la droite). En appliquant les opérations précédentes, vous obtiendrez : 
 + 
 +^ Operation ^ Resultat       ^ Commentaire                                      ^ 
 +| set       | ''0b11001111'' | les quatre derniers bits sont mit a 1            | 
 +| reset     | ''0b11000000'' | les quatre derniers bits sont mit a 0            | 
 +| flip      | ''0b11000001'' | les quatre derniers bits sont inverses           | 
 +| all       | ''false''      | les quatre derniers bits ne valent pas tous 1    | 
 +| any       | ''true''       | les quatre derniers bits contiennent au moins 1  | 
 +| none      | ''false''      | les quatre derniers bits ne valent pas 0         | 
 +| count     | ''3''          | il y a 3 bits a 1 dans les quatres derniers bits | 
 + 
 +Les bits sur les quels est appliqués ou non les modifications sont appelés le masque. En pratique, il est possible de définir n'importe quelle séquence de bits dans un masque, vous n’êtes pas limités à définir une séquence continue comme dans l'exemple précédent. 
 + 
 +Définir un masque est assez simple : il faut pouvoir définir pour chaque bit si celui-ci sera modifié (''true'') ou non (''false''). Un masque est donc en fait un ''std::bitset'', pour lequel vous affectez ''true'' aux bits qui seront pourront être modifiés. 
 + 
 +Par exemple, dans l'exemple précédent, le masque est le ''std::bitset'' suivant : ''0b00001111''. 
 + 
 +Pour appliquer le masque sur un ''std::bitset'', vous utilisez ensuite les opérateurs logiques bit à bit que vous avez déjà vu dans le chapitre [[logique_et_calcul_booleen|]] (en particulier "ET" et "OU") :  
 + 
 +  * "ET" ''&'' ("AND") ; 
 +  * "OU" ''|'' ("OR") ; 
 +  * "OU Exclusif" ''^'' ("XOR") ; 
 +  * "Negation" ''~'' ("NOT"). 
 + 
 +Pour rappel, voici le tableau récapitulatif de ces opérateurs : 
 + 
 +^  ''a''  ^  ''b''  ^  ''~a''  ^   ''a & b''  ^  ''a | b''  ^  ''a ^ b''  ^ 
 +|  0      |  0      |  1       |  0           |  0          |  0          | 
 +|  0      |  1      |  1       |  0           |  1          |  1          | 
 +|  1      |  0      |  0       |  0           |  1          |  1          | 
 +|  1      |  1      |  0       |  1           |  1          |  0          |
  
  
Ligne 294: Ligne 583:
   * trouver la plus longue chaîne de 1 dans la représentation   * trouver la plus longue chaîne de 1 dans la représentation
  
-^ [[predicats|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[valarray|Chapitre suivant]] ^+Exercices avancés (nécessite de connaitre les classes) : 
 + 
 +  * créer une classe bitset_view 
 +  * créer une classe bitset_array_view 
 + 
 +^ [[predicats|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[string_etendu|Chapitre suivant]] ^
  
bitset.1461003649.txt.gz · Dernière modification: 2016/04/18 20:20 par gbdivers