Les classes de gestion des flux constituent la deuxième hiérarchie de classes de la bibliothèque standard d'entrée / sortie. Bien que destinées à accéder à des médias variés, ces classes disposent d'une interface commune qui permet d'en simplifier l'utilisation. Cette interface est essentiellement définie par deux classes de bases fondamentales : la classe ios_base, qui définit toutes les fonctionnalités indépendantes du type de caractère utilisé par les flux, et la classe template basic_ios, qui regroupe l'essentiel des fonctionnalités des flux d'entrée / sortie.
La classe ios_base est une classe C++ classique dont toutes les classes template de gestion des flux d'entrée / sortie dérivent. Cette classe ne fournit, comme c'est le cas de la plupart des classes de base, qu'un nombre de fonctionnalités très réduit. En pratique, sa principale utilité est de définir plusieurs jeux de constantes qui sont utilisées par ses classes dérivées pour identifier les options des différents modes de fonctionnement disponibles. Ces constantes portent un nom standardisé mais leur type n'est pas précisé par la norme C++. Cependant, leur nature (entière, énumération, champ de bits) est imposée, et les implémentations doivent définir un typedef permettant de créer des variables du même type.
La classe de base ios_base est déclarée comme suit dans l'en-tête ios :
class ios_base { // Constructeur et destructeur : protected: ios_base(); public: ~ios_base(); // Classe de base des exceptions des flux d'entrée / sortie : class failure; // Classe d'initialisation des objets d'entrée / sortie standards : class Init; // Constantes de définition des options de formatage : typedef T1 fmtflags; static const fmtflags boolalpha; static const fmtflags hex; static const fmtflags oct; static const fmtflags dec; static const fmtflags fixed; static const fmtflags scientific; static const fmtflags left; static const fmtflags right; static const fmtflags internal; static const fmtflags showbase; static const fmtflags showpoint; static const fmtflags showpos; static const fmtflags uppercase; static const fmtflags unitbuf; static const fmtflags skipws; static const fmtflags adjustfield; static const fmtflags basefield; static const fmtflags floatfield; // Constantes des modes d'ouverture des flux et des fichiers : typedef T3 openmode; static const openmode in; static const openmode out; static const openmode binary; static const openmode trunc; static const openmode app; static const openmode ate; // Constantes de définition des modes de positionnement : typedef T4 seekdir; static const seekdir beg; static const seekdir cur; static const seekdir end; // Constantes d'état des flux d'entrée / sortie : typedef T2 iostate; static const iostate goodbit; static const iostate eofbit; static const iostate failbit; static const iostate badbit; // Accesseurs sur les options de formatage : fmtflags flags() const; fmtflags flags(fmtflags fmtfl); fmtflags setf(fmtflags fmtfl); fmtflags setf(fmtflags fmtfl, fmtflags mask); void unsetf(fmtflags mask); streamsize precision() const; streamsize precision(streamsize prec); streamsize width() const; streamsize width(streamsize wide); // Méthode de synchronisation : static bool sync_with_stdio(bool sync = true); // Méthode d'enregistrement des callback pour les événements : enum event { erase_event, imbue_event, copyfmt_event }; typedef void (*event_callback)(event, ios_base &, int index); void register_callback(event_call_back fn, int index); // Méthode de gestion des données privées : static int xalloc(); long &iword(int index); void* &pword(int index); // Méthodes de gestion des locales : locale imbue(const locale &loc); locale getloc() const; };
Comme vous pouvez le constater, le constructeur de la classe ios_base est déclaré en zone protégée. Il n'est donc pas possible d'instancier un objet de cette classe, ce qui est normal puisqu'elle n'est destinée qu'à être la classe de base de classes plus spécialisées.
Le premier jeu de constantes défini par la classe ios_base contient toutes les valeurs de type fmtflags, qui permettent de spécifier les différentes options à utiliser pour le formatage des données écrites dans les flux. Ce type doit obligatoirement être un champ de bits. Les constantes quant à elles permettent de définir la base de numérotation utilisée, si celle-ci doit être indiquée avec chaque nombre ou non, ainsi que les différentes options de formatage à utiliser. La signification précise de chacune de ces constantes est donnée dans le tableau suivant :
Tableau 15-1. Options de formatage des flux
Constante | Signification |
---|---|
boolalpha | Permet de réaliser les entrées / sorties des booléens sous forme textuelle et non sous forme numérique. Ainsi, les valeurs true et false ne sont pas écrites ou lues sous la forme de 0 ou de 1, mais sous la forme fixée par la classe de localisation utilisée par le flux. Par défaut, les booléens sont représentés par les chaînes de caractères « true » et « false » lorsque ce flag est actif. Cependant, il est possible de modifier ces chaînes de caractères en définissant une locale spécifique. Les notions de locales seront décrites dans le Chapitre 16. |
hex | Permet de réaliser les entrées / sorties des entiers en base hexadécimale. |
oct | Permet de réaliser les entrées / sorties des entiers en base octale. |
dec | Permet de réaliser les entrées / sorties des entiers en décimal. |
fixed | Active la représentation en virgule fixe des nombres à virgule flottante. |
scientific | Active la représentation en virgule flottante des nombres à virgule flottante. |
left | Utilise l'alignement à gauche pour les données écrites sur les flux de sortie. Dans le cas où la largeur des champs est fixée, des caractères de remplissage sont ajoutés à la droite de ces données pour atteindre cette largeur. |
right | Utilise l'alignement à droite pour les données écrites sur les flux de sortie. Dans le cas où la largeur des champs est fixée, des caractères de remplissage sont ajoutés à la gauche de ces données pour atteindre cette largeur. |
internal | Effectue un remplissage avec les caractères de remplissage à une position fixe déterminée par la locale en cours d'utilisation si la largeur des données est inférieure à la largeur des champs à utiliser. Si la position de remplissage n'est pas spécifiée par la locale pour l'opération en cours, le comportement adopté est l'alignement à droite. |
showbase | Précise la base utilisée pour le formatage des nombres entiers. |
showpoint | Écrit systématiquement le séparateur de la virgule dans le formatage des nombres à virgule flottante, que la partie fractionnaire de ces nombres soit nulle ou non. Le caractère utilisé pour représenter ce séparateur est défini dans la locale utilisée par le flux d'entrée / sortie. La notion de locale sera vue dans le Chapitre 16. Par défaut, le caractère utilisé est le point décimal ('.'). |
showpos | Utilise systématiquement le signe des nombres écrits sur le flux de sortie, qu'ils soient positifs ou négatifs. Le formatage du signe des nombre se fait selon les critères définis par la locale active pour ce flux. Par défaut, les nombres négatifs sont préfixés du symbole '-' et les nombres positifs du symbole '+' si cette option est active. Dans le cas contraire, le signe des nombres positifs n'est pas écrit. |
uppercase | Permet d'écrire en majuscule certains caractères, comme le 'e' de l'exposant des nombres à virgule flottante par exemple, ou les chiffres hexadécimaux A à F. |
unitbuf | Permet d'effectuer automatiquement une opération de synchronisation du cache utilisé par le flux de sortie après chaque écriture. |
skipws | Permet d'ignorer les blancs précédant les données à lire dans les opérations d'entrée pour lesquelles de tels blancs sont significatifs. |
La classe ios_base définit également les constantes
adjustfield
, basefield
et floatfield
,
qui sont en réalité des combinaisons des autres constantes. Ainsi, la constante
adjustfield
représente l'ensemble des options d'alignement (à savoir
left
, right
et internal
), la constante
basefield
représente les options de spécification de base pour les sorties
numériques (c'est-à-dire les options hex
, oct
et
dec
), et la constante floatfield
les options définissant
les types de formatage des nombres à virgules (scientific
et
fixed
).
Le deuxième jeu de constantes permet de caractériser les modes d'ouverture des flux et des fichiers. Le type de ces constantes est le type openmode. Il s'agit également d'un champ de bits, ce qui permet de réaliser des combinaisons entre leurs valeurs pour cumuler différents modes d'ouverture lors de l'utilisation des fichiers. Les constantes définies par la classe ios_base sont décrites dans le tableau ci-dessous :
Tableau 15-2. Modes d'ouverture des fichiers
Constante | Signification |
---|---|
in | Permet d'ouvrir le flux en écriture. |
out | Permet d'ouvrir le flux en lecture. |
binary | Permet d'ouvrir le flux en mode binaire, pour les systèmes qui font une distinction entre les fichiers textes et les fichiers binaires. Ce flag n'est pas nécessaire pour les systèmes d'exploitation conformes à la norme POSIX. Cependant, il est préférable de l'utiliser lors de l'ouverture de fichiers binaires si l'on veut que le programme soit portable sur les autres systèmes d'exploitation. |
trunc | Permet de vider automatiquement le fichier lorsqu'une ouverture en écriture est demandée. |
app | Permet d'ouvrir le fichier en mode ajout lorsqu'une ouverture en écriture est demandée. Dans ce mode, le pointeur de fichier est systématiquement positionné en fin de fichier avant chaque écriture. Ainsi, les écritures se font les unes à la suite des autres, toujours à la fin du fichier, et quelles que soient les opérations qui peuvent avoir lieu sur le fichier entre-temps. |
ate | Permet d'ouvrir le fichier
en écriture et de positionner le pointeur de fichier à la fin de celui-ci. Notez que ce mode
de fonctionnement se distingue du mode |
Le troisième jeu de constantes définit les diverses directions qu'il est possible d'utiliser lors d'un repositionnement d'un pointeur de fichier. Le type de ces constantes, à savoir le type seekdir, est une énumération dont les valeurs sont décrites dans le tableau ci-dessous :
Tableau 15-3. Directions de déplacement dans un fichier
Constante | Signification |
---|---|
beg | Le déplacement de fait par rapport au début du fichier. Le décalage spécifié dans les fonctions de repositionnement doit être positif ou nul, la valeur 0 correspondant au début du fichier. |
cur | Le déplacement se fait relativement à la position courante. Le décalage spécifié dans les fonctions de repositionnement peut donc être négatif, positif ou nul (auquel cas aucun déplacement n'est effectué). |
end | Le déplacement se fait relativement à la fin du fichier. Le décalage fourni dans les fonctions de repositionnement doit être positif ou nul, la valeur 0 correspondant à la fin de fichier. |
Enfin, les constantes de type iostate permettent de décrire les différents états dans lequel un flux d'entrée / sortie peut se trouver. Il s'agit, encore une fois, d'un champ de bits, et plusieurs combinaisons sont possibles.
Tableau 15-4. États des flux d'entrée / sortie
Constante | Signification |
---|---|
goodbit | Cette constante correspond à l'état normal du flux, lorsqu'il ne s'est produit aucune erreur. |
eofbit | Ce bit est positionné dans la variable d'état du flux lorsque la fin du flux a été atteinte, soit parce qu'il n'y a plus de données à lire, soit parce qu'on ne peut plus en écrire. |
failbit | Ce bit est positionné dans la variable d'état du flux lorsqu'une erreur logique s'est produite lors d'une opération de lecture ou d'écriture. Ceci peut avoir lieu lorsque les données écrites ou lues sont incorrectes. |
badbit | Ce bit est positionné lorsqu'une erreur fatale s'est produite. Ce genre de situation peut se produire lorsqu'une erreur a eu lieu au niveau matériel (secteur défectueux d'un disque dur ou coupure réseau par exemple). |
Les différentes variables d'état des flux d'entrée / sortie peuvent
être manipulées à l'aide de ces constantes et des accesseurs de la classe ios_base.
Les méthodes les plus importantes sont sans doute celles qui permettent de modifier les options
de formatage pour le flux d'entrée / sortie. La méthode flags
permet de récupérer
la valeur de la variable d'état contenant les options de formatage du flux. Cette méthode dispose
également d'une surcharge qui permet de spécifier une nouvelle valeur pour cette variable d'état,
et qui retourne la valeur précédente. Il est aussi possible de fixer et de désactiver les options
de formatage indépendamment les unes des autres à l'aide des méthodes setf
et unsetf
. La méthode setf
prend en paramètre les nouvelles
options qui doivent être ajoutées au jeu d'options déjà actives, avec, éventuellement, un masque permettant
de réinitialiser certaines autres options. On emploiera généralement un masque lorsque l'on voudra
fixer un paramètre parmi plusieurs paramètres mutuellement exclusifs, comme la base de numérotation
utilisée par exemple. La méthode unsetf
prend quant à elle le masque des options
qui doivent être supprimées du jeu d'options utilisé par le flux en paramètre.
Outre les méthodes de gestion des options de formatage, la classe
ios_base définit deux surcharges pour chacune des méthodes precision
et width
. Ces méthodes permettent respectivement de lire et de fixer la précision
avec laquelle les nombres à virgule doivent être écrits et la largeur minimale des conversions
des nombres lors des écritures.
La plupart des options que l'on peut fixer sont permanentes, c'est-à-dire
qu'elles restent actives jusqu'à ce qu'on spécifie de nouvelles options. Cependant, ce n'est pas le cas
du paramètre de largeur que l'on renseigne grâce à la méthode width
. En effet,
chaque opération d'écriture réinitialise ce paramètre à la valeur 0. Il faut donc spécifier la largeur
minimale pour chaque donnée écrite sur le flux.
Exemple 15-3. Modification des options de formatage des flux
#include <iostream> using namespace std; // Affiche un booléen, un nombre entier et un nombre à virgule : void print(bool b, int i, float f) { cout << b << " " << i << " " << f << endl; } int main(void) { // Affiche avec les options par défaut : print(true, 35, 3105367.9751447); // Passe en hexadécimal : cout.unsetf(ios_base::dec); cout.setf(ios_base::hex); print(true, 35, 3105367.9751447); // Affiche la base des nombres et // affiche les booléens textuellement : cout.setf(ios_base::boolalpha); cout.setf(ios_base::showbase); print(true, 35, 3105367.9751447); // Affiche un flottant en notation à virgule fixe // avec une largeur minimale de 16 caractères : cout << "***"; cout.width(16); cout.setf(ios_base::fixed, ios_base::floatfield); cout << 315367.9751447; cout << "***" << endl; // Recommence en fixant la précision // à 3 chiffres et la largeur à 10 caractères : cout << "***"; cout.precision(3); cout.width(10); cout << 315367.9751447; cout << "***" << endl; return 0; }
Note : On prendra bien garde au fait que la largeur des champs dans lesquels les données sont écrites est une largeur minimale, pas une largeur maximale. En particulier, cela signigie que les écritures ne sont pas tronquées si elles sont plus grande que cette largeur. On devra donc faire extrêmement attention à ne pas provoquer de débordements lors des écritures.
On n'oubliera pas de s'assurer de la cohérence des paramètres du flux lorsqu'on modifie la valeur d'une option. Par exemple, dans l'exemple précédent, il faut désactiver l'emploi de la numérotation décimale lorsque l'on demande à utiliser la base hexadécimale. Cette opération a été faite explicitement ici pour bien montrer son importance, mais elle aurait également pu être réalisée par l'emploi d'un masque avec la constante
ios_base::basefield
. L'exemple précédent montre comment utiliser un masque avec l'appel àsetf
pour fixer la représentation des nombres à virgule.
La classe ios_base fournit également un certain nombre de
services généraux au programmeur et à ses classes dérivées. La méthode sync_with_stdio
permet de déterminer, pour un flux d'entrée / sortie standard, s'il est synchronisé avec le flux
sous-jacent ou si des données se trouvent encore dans son tampon. Lorsqu'elle est appelée avec
le paramètre false dès le début du programme, elle permet de décorréler
le fonctionnement du flux C++ et du flux standard sous-jacent. Pour tous les autres appels, cette méthode
ignore le paramètre qui lui est fourni. La méthode register_callback
permet
d'enregistrer une fonction de rappel qui sera appelée par la classe ios_base lorsque
des événements susceptibles de modifier notablement le comportement du flux se produisent. Ces fonctions
de rappel peuvent recevoir une valeur entière en paramètre qui peut être utilisée pour référencer
des données privées contenant des paramètres plus complexes. Les méthodes xalloc
,
iword
et pword
sont fournies afin de permettre de stocker
ces données privées et de les retrouver facilement à l'aide d'un indice, qui peut être la valeur
passée en paramètre à la fonction de rappel. Ces méthodes permettent de récupérer des références
sur des valeurs de type long et sur des pointeurs de type void. Enfin,
la classe ios_base fournit une classe de base pour les exceptions que les classes
de flux pourront utiliser afin de signaler une erreur et une classe permettant d'initialiser
les objets cin, cout, cerr, clog
et leurs semblables pour les caractères larges. Toutes ces fonctionnalités ne sont généralement
pas d'une très grande utilité pour les programmeurs et sont en réalité fournie pour faciliter
l'implémentation des classes de flux de la bibliothèque standard.
Enfin, la classe ios_base fournit les méthodes
getloc
et imbue
qui permettent respectivement
de récupérer la locale utilisée par le flux et d'en fixer une autre. Cette locale est utilisée
par le flux pour déterminer la manière de représenter et de lire les nombres et pour effectuer
les entrées / sorties formatées en fonction des paramètres de langue et des conventions locales
du pays où le programme est exécuté. Les notions de locale et de paramètres internationaux seront
décrits en détail dans le Chapitre 16.
La classe template basic_ios fournit toutes les fonctionnalités communes à toutes les classes de flux de la bibliothèque d'entrée / sortie. Cette classe dérive de la classe ios_base et apporte tous les mécanismes de gestion des tampons pour les classes de flux. La classe basic_ios est déclarée comme suit dans l'en-tête ios :
template <class charT, class traits = char_traits<charT> > class basic_ios : public ios_base { // Constructeur et destructeur : protected: basic_ios(); void init(basic_streambuf<charT,traits> *flux); public: // Types de données : typedef charT char_type; typedef typename traits::int_type int_type; typedef typename traits::pos_type pos_type; typedef typename traits::off_type off_type; typedef traits traits_type; // Constructeur publique, destructeur et opération de copie : explicit basic_ios(basic_streambuf<charT,traits> *flux); virtual ~basic_ios(); basic_ios ©fmt(const basic_ios &); // Méthodes de gestion des tampons : basic_streambuf<charT,traits> *rdbuf() const; basic_streambuf<charT,traits> *rdbuf( basic_streambuf<charT,traits> *tampon); // Méthodes de gestion des exceptions : iostate exceptions() const; void exceptions(iostate except); // Accesseurs : operator void*() const bool operator!() const iostate rdstate() const; void clear(iostate statut = goodbit); void setstate(iostate statut); bool good() const; bool eof() const; bool fail() const; bool bad() const; char_type fill() const; char_type fill(char_type c); basic_ostream<charT,traits> *tie() const; basic_ostream<charT,traits> *tie( basic_ostream<charT,traits> *flux); // Méthodes de gestion des locales : locale imbue(const locale &loc); char narrow(char_type c, char defaut) const; char_type widen(char c) const; };
Le constructeur de base ainsi que la méthode init
,
destinée à associer un tampon à un flux après sa construction, sont déclarés en zone protégée. Ainsi,
il n'est pas possible d'instancier et d'initialiser manuellement un flux avec un tampon. Cependant,
la classe basic_ios fournit un constructeur en zone publique qui permet de créer
un nouveau flux et de l'initialiser à la volée. Ce constructeur prend en paramètre l'adresse
d'un tampon qui sera associé au flux après la construction. Remarquez que, bien qu'il soit ainsi
parfaitement possible d'instancier un objet de type l'une des instances de la classe
template basic_ios, cela n'a pas grand intérêt. En effet, cette classe
ne fournit pas assez de fonctionnalités pour réaliser des entrées / sorties facilement sur le flux.
La classe basic_ios est donc réellement destinée à être utilisée en tant que classe de
base pour des classes plus spécialisées dans les opérations d'entrée / sortie.
Une fois initialisés, les flux sont associés aux tampons grâce auxquels
ils accèdent aux médias physiques pour leurs opérations d'entrée / sortie. Cependant, cette association
n'est pas figée et il est possible de changer de tampon a posteriori. Les manipulations de tampon
sont effectuées avec les deux surcharges de la méthode rdbuf
. La première permet
de récupérer l'adresse de l'objet tampon courant et la deuxième d'en spécifier un nouveau. Cette
dernière méthode renvoie l'adresse du tampon précédent. Bien entendu, le fait de changer le tampon
d'un flux provoque sa réinitialisation.
Les flux de la bibliothèque standard peuvent signaler les cas d'erreurs
aux fonctions qui les utilisent de différentes manières. La première est simplement de renvoyer
un code d'erreur (false ou le caractère de fin de fichier), et la deuxième
est de lancer une exception dérivée de la classe d'exception failure (définie dans la classe
de base ios_base). Ce comportement est paramétrable en fonction des types d'erreurs qui
peuvent se produire. Par défaut, les classes de flux n'utilisent pas les exceptions, quelles
que soient les erreurs rencontrées. Toutefois, il est possible d'activer le mécanisme des exceptions
individuellement pour chaque type d'erreur possible. La classe basic_ios gère pour cela
un masque d'exceptions qui peut être récupéré et modifié à l'aide de deux méthodes surchargées.
Ces méthodes sont les méthodes exceptions
. La première version renvoie le masque
courant et la deuxième permet de fixer un nouveau masque. Les masques d'exceptions sont constitués
de combinaisons logiques des bits d'état des flux définis dans la classe ios_base (à savoir
goodbit
, eofbit
, failbit
et
badbit
). Le fait de changer le masque d'exceptions réinitialise l'état du flux.
La classe basic_ios fournit également tout un ensemble
d'accesseurs grâce auxquels il est possible de récupérer l'état courant du flux. Ces accesseurs sont
principalement destinés à faciliter la manipulation du flux et à simplifier les différentes expressions
dans lesquelles il est utilisé. Par exemple, l'opérateur de transtypage vers le type pointeur sur
void permet de tester la validité du flux comme s'il s'agissait d'un pointeur. Cet opérateur
retourne en effet une valeur non nulle si le flux est utilisable (c'est-à-dire si la méthode
fail
renvoie false. De même, l'opérateur de négation
operator!
renvoie la même valeur que la méthode fail
.
Comme vous l'aurez sans doute compris, la méthode fail
indique si le flux (et donc le tampon contrôlé par ce flux) est dans un état correct. En pratique,
cette méthode renvoie true dès que l'un des bits ios_base::failbit
ou ios_base::badbit
est positionné dans la variable d'état du flux. Vous pourrez
faire la distinction entre ces deux bits grâce à la méthode bad
, qui elle ne renvoie
true que si le bit ios_base::badbit
est positionné. Les autres
méthodes de lecture de l'état du flux portent des noms explicites et leur signification ne doit pas poser
de problème. On prendra toutefois garde à bien distinguer la méthode clear
, qui permet
de réinitialiser l'état du flux avec le masque de bits passé en paramètre, de la méthode
setstate
, qui permet de positionner un bit complémentaire. Ces deux méthodes
sont susceptibles de lancer des exceptions si le nouvel état du flux le requiert et si son masque
d'exceptions l'exige.
Le dernier accesseur utile pour le programmeur est l'accesseur
fill
. Cet accesseur permet de lire la valeur du caractère de remplissage
utilisé lorsque la largeur des champs est supérieure à la largeur des données qui doivent être
écrites sur le flux de sortie. Par défaut, ce caractère est le caractère d'espacement.
Note : Les deux surcharges de la méthode
tie
permettent de stocker dans le flux un pointeur sur un flux de sortie standard avec lequel les opérations d'entrée / sortie doivent être synchronisées. Ces méthodes sont utilisées en interne par les méthodes d'entrée / sortie des classes dérivées de la classe basic_ios et ne sont pas réellement utiles pour les programmeurs. En général donc, seule les classes de la bibliothèque standard les appelleront.
Enfin, la classe basic_ios prend également en compte
la locale du flux dans tous ses traitements. Elle redéfinit donc la méthode imbue
afin de pouvoir détecter les changement de locale que l'utilisateur peut faire. Bien entendu,
la méthode getloc
est héritée de la classe de base ios_base
et permet toujours de récupérer la locale courante. De plus, la classe basic_ios définit
deux méthodes permettant de réaliser les conversions entre le type de caractère char
et le type de caractère fourni en paramètre template. La méthode
widen
permet, comme son nom l'indique, de convertir un caractère de type
char
en un caractère du type template du flux. Inversement,
la méthode narrow
permet de convertir un caractère du type de caractère du flux
en un caractère de type char. Cette méthode prend en paramètre le caractère à convertir
et la valeur par défaut que doit prendre le résultat en cas d'échec de la conversion.
Précédent | Sommaire | Suivant |
Les tampons | Niveau supérieur | Les flux d'entrée / sortie |