5.3.2a ios_base
"Class ios is bigger, more diffuse, and trickier than average". P.J. Plauger. Coordinador de los comités ISO para estandarización de C y C++. Cpp User Journal Mayo 1994.
§1 Sinopsis
El manejo de E/S en la Librería Estándar C++, se realiza mediante objetos que derivan de una jerarquía de clases de la que, como indica su nombre, ios_base es la raíz. En realidad esta clase es pura interfaz, de forma que no se utiliza para instanciar objetos, sino como raíz de la que derivan las demás. Los objetos derivados de ella (de sus clases derivadas) se denominan de forma genérica iostreams y junto a los streambuf ( 5.3.2f), componen la esencia del mecanismo C++ de E/S. Como el resto de miembros de la STL, ios_base está declarada en el espacio de nombres std ( 4.1.11c2); sus definiciones están en la cabecera <ios>.
ios_base se encarga del formateo de datos que entran y salen del buffer de flujo, así como de encapsular ciertos detalles y características del flujo y del buffer asociado. Para ello dispone de miembros que realizan las siguientes tareas:
Información de control para análisis ("parsing"); forma de interpretar los flujos de entrada y forma de generar la secuencia de salida (formateos de entrada y salida).
-
Información adicional para necesidades específicas de usuario. Por ejemplo para extender los servicios de la jerarquía.
-
El localismo imbuido en el flujo (en forma de un objeto de la clase locale 5.2).
-
Información adicional privada para su propio uso.
-
Una clase failure, derivada de la clase genérica de excepciones C++ exception, utilizada para instanciar los objetos lanzados por el mecanismo de excepciones.
Esta superclase también define diversos tipos utilizados por toda la jerarquía, como indicadores de formato, bits de estado, formas de apertura, Etc. Por ejemplo, contiene diversos miembros de los tipos iostate y openmode que almacenan información de estado del dispositivo físico relacionado con el flujo (fichero de disco; puerto de comunicaciones; dirección de red, Etc.)
§2 Interfaz
class ios_base {
public: // miembros públicos
static const fmtflags boolalpha;
static const fmtflags dec;
static const fmtflags fixed;
static const fmtflags hex;
static const fmtflags internal;
static const fmtflags left;
static const fmtflags oct;
static const fmtflags right;
static const fmtflags scientific;
static const fmtflags showbase;
static const fmtflags showpoint;
static const fmtflags showpos;
static const fmtflags skipws;
static const fmtflags unitbuf;
static const fmtflags uppercase;
static const fmtflags adjustfield;
static const fmtflags basefield;
static const fmtflags floatfield;
static const iostate badbit;
static const iostate eofbit;
static const iostate failbit;
static const iostate goodbit;
static const openmode app;
static const openmode ate;
static const openmode binary;
static const openmode in;
static const openmode out;
static const openmode trunc;
static const seekdir beg;
static const seekdir cur;
static const seekdir end;
locale imbue(const locale& loc);
locale getloc() const;
// Control del almacenamiento de usuario
static int xalloc();
long& iword(int index);
void*& pword(int index);
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étodos para controlar aspectos del formato
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);
// Sincronización de flujos
static bool sync_with_stdio(bool sync = true);
virtual ~ios_base(); // destructor virtual
protected: // miembros protegidos
ios_base(); // constructor
};
§2.1 Comentario.
Además de ser la raíz, es la única de la jerarquía que no es una clase genérica (plantilla 4.12.2). Declara 4 tipos T1, T2, T3 y T4 que son dependientes de la implementación, pero tienen su correspondiente alias estándar, de forma que pueden ser accedidos siempre con el mismo nombre. Son respectivamente: fmtflags; iostate; openmode y seekdir, a los que tendremos ocasión de referirnos repetidamente.
Basados en estos tipos se declaran cuatro grupos de propiedades (constantes estáticas). Como veremos a continuación, cada grupo controla un aspecto del comportamiento del objeto iostream.
En lo que sigue nos referimos a la forma concreta de implementación utilizada por C++Builder (que podemos considerar representativa). En este compilador, los tipos de T1, T2, T3 y T4 son int, y los cuatro grupos de propiedades son constantes de enumeración (se definen mediante enumeraciones 4.8).
§3 fmtflags
El primer grupo de miembros se define mediante una enumeración cuyas constantes conforman en realidad una máscara de bits [1].
enum fmt_flags {
boolalpha, dec, fixed, hex, internal, left, oct,
right, scientific, showbase, showpoint, showpos, skipws,
unitbuf, uppercase, adjustfield, basefield, floatfield
};
Existe una propiedad interna fmtflags, a la que denominaremos máscara de formato, cuyos bits controlan aspectos del formato del flujo. Por ejemplo, si su valor es tal que:
fmtflags == dec | left | uppercase;
este patrón tiene un significado concreto en el formato del flujo. La máscara de formato permite especificar aspectos de las E/S numéricas tales como:
-
Base de representación a utilizar (existen tres posibilidades: decimal, hexadecimal y octal) y si se añadirá o no un prefijo indicativo del tipo que se está utilizando (0 para octal y 0X / 0x para hexadecimal).
-
Si se utilizará un indicativo explícito (+) para los números positivos.
-
Ancho mínimo del campo: Como se manejarán los enteros que contengan menos dígitos que el ancho especificado y, en su caso, como se rellenarán los huecos faltantes (a la izquierda, entre el signo y el primer dígito o a la derecha).
-
Como se representarán las cantidades fraccionarias.
-
Etc. Etc.
El significado de las fmt_flags es el indicado en la tabla adjunta.
Elemento | default | Efecto en la E/S. |
boolalpha | off | Transforma los tipos bool (0/1) a formato alfabético (true / false) |
dec | on | Transforma los enteros a formato decimal ( 0.1). Ej. 123 |
fixed | off | Transforma las cantidades fraccionarias en notación de punto decimal fijo. Ej. 123.00 |
hex | off | Transforma los enteros a formato hexadecimal ( 2.2.4b) Ej. 0x7b |
internal | off | Añade caracteres de relleno en ciertos lugares de ciertas salidas, o a la derecha si tales lugares no han sido especificados. |
left | off | Añade caracteres de relleno a la izquierda en ciertas salidas. " 123" |
oct | off | Transforma los enteros a formato octal ( 2.2.4b) Ej. 0173 |
right | off | Añade caracteres de relleno a la derecha en ciertas salidas. "123 " |
scientific | off | Transforma las cantidades fraccionarias en notación científica. Ej. 12.3E4 |
showbase | off | Añade a las salidas un prefijo indicativo de la base de numeración utilizada para los enteros. 0 para octal 0x/0X para hexadecimal. |
showpoint | off | En las salidas, añade el punto (o símbolo) decimal a las cantidades fraccionarias de forma incondicional (aunque no tengan parte fraccionaria). Ej. 123.0 Los enteros no sufren modificación. |
showpos | off | Añade un signo + a las salidas numéricas que no sean negativas. Ej: +123 |
skipws | on | Descarta ciertos caracteres "Whitespaces" previos en determinadas operaciones de entrada ( 5.3.3b) |
unitbuf | off | Vacía del buffer después de cada operación de salida (fuerza la escritura inmediata en el dispositivo correspondiente 5.3.2f). |
uppercase | off | Transforma ciertas minúsculas en las mayúsculas equivalentes en las salidas (E/e de la notación científica y letras a-z/A-Z de la notación hexadecimal). |
adjustfield |
Esta constante puede adoptar alguno de los valores: left | right |
internal. internal significa que se incluirá un espacio entre el signo y el primer dígito en las cantidades numéricas. Ej: - 12.3 |
|
basefield | dec | Puede adoptar alguno de los valores dec | hex | oct. |
floatfield |
Puede adoptar alguno de los valores scientific | fixed scientific indica que se utilizará el exponente d decimal Ed/ed fixed indica que no se utilizará el exponente decimal Seteando ambos valores se elige la mejor representación posible para el número. Nota: Las tres combinaciones posibles para este campo se corresponden con los tres valores: e (científico), f (fijo) y g (otro), del especificador de conversión de la función fprinf() de la librería clásica. |
Para referirse de forma individual a estas constantes, generalmente se utiliza el alias ios ( 5.3.2) de su clase derivada basic_ios, en vez de su nombre real (por supuesto las propiedades de ios_base están presente en su hija basic_ios). Las expresiones que siguen son equivalentes, aunque es más frecuente utilizar la segunda:
if (format & ios_base::boolalpha) cout << "Formato boolalfa!!";
if (format & ios::boolalpha) cout << "Formato boolalfa!!";
Salvo indicación explícita en contrario, en las expresiones y ejemplos que siguen, se supone que nos referimos al espacio de nombres std en que están definidos los miembros de las librerías estándar C++, de forma que en la expresión anterior debemos entender en realidad algunas de las sintaxis siguientes:
if (format & std::ios_base::boolalpha) std::cout << "Formato boolalfa!!";
if (format & std::ios::boolalpha) std::cout << "Formato boolalfa!!";
o en su defecto:
using namespace std;
if (format & ios_base::boolalpha) cout << "Formato boolalfa!!";
if (format & ios::boolalpha) cout << "Formato boolalfa!!";
Ejemplos
Las sentencias que siguen pretenden mostrar la influencia de las constantes de la máscara de formato [3]. Ver más adelante la descripción de los métodos utilizados y de la función auxiliar watFormat ( 5.3.2a3).
int pos = 58, neg = -27;
ios_base::fmtflags format = cout.flags();
watFormat(format);
// -> dec; skipws; basefield:
cout << "ints: " << pos << "; " << neg << endl;
// -> ints: 58; -27
cout.flags(ios::hex);
watFormat(cout.flags());
// -> hex; basefield:
cout << "ints: " << pos << "; " << neg << endl;
// -> ints: 3a; ffffffe5
Observe que el valor hexadecimal obtenido para neg (229 decimal) corresponde en realidad al valor de su complemento a dos ( 2.2.4a).
cout.flags(cout.flags() | ios::uppercase);
cout << "ints: " << pos << "; " << neg << endl;
// -> ints: 3A; FFFFFFE5
cout.flags(cout.flags() | ios::showbase );
cout << "ints: " << pos << "; " << neg << endl;
// -> ints: 0X3A; 0XFFFFFFE5
float f1 = 3.14, f2 = -3.00;
cout << "floats: " << f1 << "; " << f2 << endl;
// -> floats: 3.14; -3
cout.flags(cout.flags() | ios::showpoint );
// 6 cifras por defecto
cout << "floats: " << f1 << "; " << f2 << endl;
// -> floats: 3.14000; -3.00000
cout.flags(cout.flags() | ios::showpos );
cout << "floats: " << f1 << "; " << f2 << endl;
// -> floats: +3.14000; -3.00000
cout.flags(cout.flags() | ios::fixed );
// 6 decimales por defecto
watFormat(cout.flags());
// -> fixed; hex; showbase; showpoint; showpos; uppercase; basefield: floatfield:
cout << "floats: " << f1 << "; " << f2 << endl;
// -> floats: 3.140000; -3.000000
cout.flags( ios::scientific | ios::uppercase);
watFormat(cout.flags());
// -> scientific; uppercase; floatfield:
cout << "floats: " << f1 << "; " << f2 << endl;
// -> floats: 3.140000E+00; -3.000000E+00
§4 state
El segundo grupo se define también mediante una enumeración que también es una máscara de bits ("bitmask"):
enum io_state { goodbit, badbit, eofbit, failbit };
Esta enumeración no se designa por su verdadero nombre (io_state), sino mediante un alias: iostate (un typedef). Existe una propiedad state de este tipo cuyo valor controla algunos aspectos del flujo según el valor de sus bits que tienen los significados siguientes:
-
goodbit Flujo Ok. integridad correcta (ninguno de los siguientes es cierto).
-
badbit Indica error de L/E o pérdida de integridad en la secuencia del flujo.
-
eofbit Indica que la operación ha alcanzado el final de una secuencia (por ejemplo fin de fichero).
-
failbit Indica que la operación de entrada ha no ha podido leer los caracteres esperados, o que una salida no ha podido generar los caracteres deseados.
El valor de state puede ser interrogado mediante el método rdstate ( 5.3.2b); aunque el valor devuelto debe ser interpretado analizando sus bits individuales mediante las constantes anteriores. Como veremos a continuación, algunos métodos chequean directamente sus bits; otros en cambio se basan en estos valores para su funcionamiento. Por ejemplo:
-
eof( 5.3.2b) devuelve true si el eofbit está seteado (1) y false en caso contrario (0).
-
good( 5.3.2b) devuelve true si los demás bits están a cero (todos los demás son false).
-
fail( 5.3.2b) devuelve true si está seteado (1) failbit o badbit. false en caso contrario (0).
-
bad( 5.3.2b) devuelve true si batbit está seteado (1).
§5 openmode
Son pertinentes las observaciones anteriores; el tercer grupo de miembros se describe mediante una máscara que se suele utilizar por su alias, openmode, en sustitución de su verdadero nombre:
enum open_mode { app, binary, in, out, trunc, ate } ;
Existe una propiedad de este tipo cuyos bits controlan la forma de apertura del fichero que se asociará al flujo. El significado de sus bits es el siguiente:
-
app Desplazar ("seek") al final del fichero antes de escribir (añadir al final).
-
ate Abrir y desplazar al final del fichero inmediatamente después.
-
binary Relizar las operaciones E/S de flujo en modo binario.
-
in Abrir en modo lectura ("Input").
-
out Abrir para escritura ("Output").
-
trunc Modo de sobreescritura. Borrar cualquier contenido previo del fichero.
Nota: Esta propiedad es manejada indirectamente, a través del método open() disponible en cada una de las subclases previstas para manejar ficheros ( 5.3.3).
La tabla adjunta muestra la correspondencia entre las formas de apertura STL y las utilizadas por las rutinas fopen de la librería clásica (observe que no existe equivalente "clásico" al modo ate).
bynary | in | out | trunc | app | stdio equivalente | Descripción |
+ | "w" | Abrir para escritura. Si el fichero existe con anterioridad será sobreescrito. Si no existe será creado. En la librería clásica, el modo (binario o texto) depende de la variable globlal _fmode. En la STL es modo texto. | ||||
+ | + | "a" | Abrir en modo escritura para añadir al final. Si el fichero no existe es creado. Idénticas consideraciones sobre el modo (binario/texto) que en el caso anterior. | |||
+ | + | "w" | Abrir para escritura. Si el fichero existe con anterioridad será sobreescrito. Si no existe será creado. Idem. Idem. respecto al modo. | |||
+ | "r" | Abrir para lectura. Si el fichero no existe se produce un error. Idem. Idem. respecto al modo. | ||||
+ | + | "r+" | Abrir un fichero existente para lectura y escritura. Si el fichero no existe se produce un error. Idem. Idem. respecto al modo. | |||
+ | + | + | "w+" | Abrir para lectura/escritura. Si el fichero existe con anterioridad será sobreescrito. Si no existe será creado. Idem. Idem. respecto al modo. | ||
+ | + | "wb" | Abrir para escritura en modo binario. Si el fichero existe con anterioridad será sobreescrito. Si no existe será creado. | |||
+ | + | + | "ab" | Abrir en modo escritura binaria para añadir al final. Si el fichero no existe es creado. | ||
+ | + | + | "wb" | Abrir para escritura en modo binario. Si el fichero existe con anterioridad será sobreescrito. Si no existe será creado. | ||
+ | + | "rb" | Abrir para lectura en modo binario. Si el fichero no existe se produce un error. | |||
+ | + | + | "r+b" | Abrir un fichero existente para lectura y escritura en modo binario. | ||
+ | + | + | + | "w+b" | Abrir para lectura/escritura en modo binario. Si el fichero existe con anterioridad será sobreescrito. Si no existe será creado. |
§6 seek_dir
El cuarto grupo de miembros responde a las mismas características que los anteriores (también enumeración) de alias seekdir que responde a la declaración siguiente:
enum seek_dir { beg, cur, end };
Existe una propiedad de este tipo cuyo valor define la forma en que se realizarán los desplazamientos del puntero interno ("file pointer") en los procesos de lectura y escritura de la capa de transporte. La propiedad es controlada de forma indirecta, mediante los métodos open() y seekg() de las subclases que tienen que ver con las E/S a ficheros ( 5.3.3). El significado de sus bits es el indicado (entre paréntesis el modo equivalente de la librería clásica):
-
beg Solicitar un desplazamiento ("seek") del puntero respecto del principio del fichero (SEEK_SET).
-
cur Solicitar un desplazamiento respecto de la posición actual (SEEK_CUR).
-
end Solicitar un desplazamiento respecto del final del fichero (SEEK_END).
§7 failure
Como se ha indicado, cuando el sistema de E/S necesita lanzar una excepción, el mensajero lanzado ("trhown") es una instancia de esta clase que deriva de exception, la clase general de excepciones C++ ( 1.6.1a). Responde a la siguiente interfaz:
class ios_base::failure : public exception {
public:
explicit failure(const string& msg);
virtual ~failure();
virtual const char* what() const throw();
};
Como puede verse tiene un constructor explícito y un destructor virtual (algo muy común en esta jerarquía). El argumento del constructor puede utilizarse para incluir el mensaje que será incluido en la excepción.
§8 Localismos
Hemos señalado ( 5.3) que la capa de formato (también la de transporte) utiliza el mecanismo C++ de internacionalización ( 5.2), de forma que delega en los localismos numéricos para el manejo de formatos. Con este fin, todos los objetos iostreams disponen de un objeto locale asociado (aquí se utiliza el término "imbuido").
Por defecto se utilizan las directrices del locale global, pero pueden indicarse otras (el hecho de asociar un locale a un iostream se denomina imbuir). Esto puede hacerse de dos formas: en el momento de creación del objeto iostream, o mediante un método específico. También se dispone de un método para averiguar que locale está imbuido en un objeto determinado. Son los siguientes:
locale getloc() const;
Este método devuelve el locale actual (que se está utilizando para realizar los formateos de E/S que dependen de localismos). Ejemplo:
ifstream stream(oldFile); // Un fichero cualquiera
locale localismoActual = stream.getloc();
cout << "Localismo actual: " << localismoActual.name(); // -> C
locale imbue(const locale& loc);
Este método guarda el locale actual (valor devuelto por el método anterior); a continuación asigna el locale loc (pasado por referencia) a una propiedad privada. Acto seguido, invoca las funciones que hayan sido registradas con register_callback( 5.3.2a2) para el objeto en cuestión. Finalmente devuelve el locale guardado inicialmente (que se estaba utilizando antes de imbuir el nuevo). Ejemplo:
cout.imbue(locale::locale("english-uk"));
§9 Init
La norma establece que los flujos estándar ( 5.3) serán creados antes que se construya ningún otro objeto basic_ios, y en cualquier caso, antes que se inicie la ejecución de la función main. Con este fín, ios_base incluye una clase auxiliar Init, que es utilizada para instanciar un objeto cuya construcción asegura a su vez la adecuada inicialización de los 8 flujos declarados en <iostream>. Por razones de sincronización, estos objetos asocian los almacenamientos intermedios (streambuf) de los iostreams con los almacenamientos de los flujos estándar C definidos en <cstdio>.
Con objeto de que no puedan crearse nuevos flujos estándar (que son únicos y duran toda la vida del programa) después de que se haya instanciado el primer objeto ios_base, la clase Init mantiene un contador interno basado en una propiedad estática (única para todas las instancias de la clase) que se incrementa en una unidad con cada nueva instancia creada. Si el contador es uno, se crean e inician los objetos cin, cout, cerr, clog, wcin, wcout, wcerr y wclog. De forma similar, el destructor decrementa este contador en una unidad cada vez que es destruido un flujo. Cuando se alcanza el valor 1, se invocan los métodos flush( 5.3.2d) de los flujos estándar relacionados con salidas: cout, cerr, clog, wcout, wcerr y wclog.
§10 Almacenamiento
La librería IOStream dispone de recursos para almacenar información de usuario en los propios objetos iostream. El sistema consiste en un área de almacenamiento en cada objeto, conocida como parray, y una serie de utilidades auxiliares para su manejo. Aunque todos estos recursos están relacionados entre si, dividiremos su estudio en dos secciones. La primera, que comprende la esencia del sistema ( 5.3.2a1), está representada por tres métodos públicos de ios_base:
long& iword(int idx);
void*& pword(int idx);
static int xalloc();
La segunda, una aportación posterior, incluye un sistema de retrollamadas ("callbacks") que resuelve ciertos peligros potenciales que, en determinadas circunstancias, podían derivarse de los métodos anteriores ( 5.3.2a2).
Nota: En teoría la información a almacenar en el parray puede ser cualquiera, aunque el sistema está pensado para guardar información relativa a formato, y arbitrando mecanismos para que pueda ser compartida por todos los iostreams de una aplicación.
§11 Control de formato
Los aspectos del formato que dependen de variables booleanas, valores cierto/falso, están controlados por la máscara de bits de la propiedad fmtflags , pero no se manejan directamente, sino a través de los siguientes métodos públicos:
fmtflags flags() const;
fmtflags flags(fmtflags fmtfl);
fmtflags setf(fmtflags fmtfl);
fmtflags setf(fmtflags fmtfl, fmtflags mask);
void unsetf(fmtflags mask);
Existen otros aspectos del formato que dependen de variables numéricas enteras, por lo que no tienen representación en ningún bit de la máscara. Estas variables son controladas por un par de métodos auxiliares:
streamsize precision() const;
streamsize precision(streamsize prec);
streamsize width() const;
streamsize width(streamsize wide);
En el capítulo dedicado a Control de Formato se detallan las particularidades de ambos grupos de métodos ( 5.3.2a3)
§14 Sincronización
bool sync_with_stdio(bool sync = true);
Este método que permite controlar la sincronización entre los flujos estándar, proporcionados por las funciones de la cabecera clásica <cstdio> y los iostreams definidos en <ios>.
El metodo acepta un bool como argumento (con un valor por defecto que es true) y devuelve así mismo un bool. Cuando el argumento es false, permite que los flujos estándar C++ funcionen independientemente de los flujos estándar C (funcionen de forma asíncrona). Cuando el argumento es true, se restablece la sincronización. El valor devuelto es el estado de sincronización anterior a la invocación.
Por las razones señaladas a hablar de los flujos estándar de E/S ( 5.3), el funcionamiento asíncrono incrementa el rendimiento. Cualquier aplicación funciona de este modo, al menos tan rápido como en forma síncrona y en la mayoría de los casos mucho más rápido. Por esta razón sin los aspectos del rendimiento son importantes y la aplicación no requiere el uso simultáneo de las rutinas E/S clásicas y de la STL, es recomendable deshacer el modo síncrono (por defecto) mediante una invocación a este método al principio del programa:
std::ios_base::sync_with_stdio(false);
En muchos casos también es posible añadir una invocación al método tie ( 5.3.2b) de basic_ios [6] para eliminar cualquier sincronización que pudiera existir en el flujo estándar de entrada.
std::cin.tie(0);
§15 Constructor
Como puede verse el constructor de la clase es un miembro protegido, lo que significa que no es posible instanciar directamente objetos ios_base. Es su clase derivada basic_ios, la primera de la jerarquía que dispone de un constructor público explícito, y aun así, es un caso bastante atípico, ya que la inicialización de miembros no está encomendada a constructor, sino a una función auxiliar init ( 5.3.2b). Tendremos ocasión de ver que es esta función la encargada de iniciar muchas de las propiedades de ios_base.
La clase no dispone de un constructor-copia ( 4.11.2d4) lo que tiene importantes consecuencias prácticas: no es posible pasar un iostream como argumento de una función, ya que no es posible copiar el objeto. Por ejemplo:
void foo(std::ifstream stream) { ... }
...
int main() {
std::ifstream myfile ("somefile.txt);
foo (myfile); // M.2 Error!!
...
}
En M.2 se obtiene un error de compilación: "Compiler could not generate copy constructor for
class 'ios'
". La razón es bastante evidente: los flujos
representan conexiones físicas con dispositivos y no pueden ser duplicados
alegremente. La solución en estos casos en pasar referencias al objeto
(
4.4.5). En el caso anterior lo correcto sería:
void foo(std::ifstream& stream) { ... }
...
int main() {
std::ifstream myfile ("somefile.txt);
foo (myfile); // Ok.
...
}
Nota: La subclase basic_ios dispone de un método, copyfmt ( 5.3.2b) que, en cierta forma, permite remediar la falta de un constructor-copia.
[1] Observe que, en contra de lo que es usual en el mundo C++, los nombres de las constantes de enumeración no están aquí en mayúsculas.
[3] Las salidas corresponden a C++Builder 6.
[6] De acuerdo con el artículo "IOStreams and Stdio" del mentado Matthew Austern en Cpp Users Journal de Noviembre 2000.