5.3.2 Jerarquía de clases estándar de E/S
"I/O is inherently complicated, and the C++ Standard library reflects that complexity. The C++ Standard defines ten I/O headers, dozens of I/O classes, and hundreds of member and nonmember functions". Matt Austern. Presidente del grupo de trabajo de la Librería Estándar del Comité de Estandarización del C++. Cpp Users Journal, Noviembre 2000.
§1 Presentación
En el presente capítulo mostramos una visión sinóptica de las jerarquías de clases que componen los servicios estándar C++ de entradas/salidas, que hemos designado con el nombre genérico de iostreams.
§2 Sinopsis
Hemos señalado ( 5.3) que las rutinas C++ que manejan los flujos de E/S siguen una arquitectura de dos capas, denominadas de formato y de transporte y que las clases que sirven estos algoritmos se organizan en dos jerarquías según la capa que sirven. También hemos indicado que estas clases encapsulan objetos de tipo locale, que se encargan de manejar los localismos relativos a formateos, "parsing", y conversiones de código.
Para agilizar las operaciones, estos algoritmos se apoyan en un almacenamiento temporal, denominado genéricamente buffer de flujo ("Stream buffer") que recibe diversos nombres: buffer de fichero ("file buffer") cuando son flujos a ficheros de disco y buffer de cadena ("string buffer") cuando son flujos a memoria. Los designaremos abreviadamente como streambuf, filebuf y stringbuf.
Digamos también, que a excepción de una (ios_base), las demás clases de estas jerarquías son en realidad clases genéricas (plantillas) que se especializan según el tipo y peculiaridades del flujo a manejar ( 4.12.2).
§3 Peculiaridades del flujo
Existen ciertas peculiaridades ("Traits") de los flujos que deben ser conocidos por los algoritmos que los manejan y que varían de un flujo a otro en función del tipo de "caracteres" que se manejan. Por ejemplo:
-
Valor de fin de fichero: Para el tipo char, este valor está representado por una constante entera denominada EOF ("End of File"). Para el tipo wchar_t esta constante se denomina WEOF. Para cualquier otro tipo definido por el usuario, debe ser conocido cual es el valor de fin de fichero para el tipo particular.
-
Tipo del valor EOF: Debe ser un tipo que pueda albergar el valor de esta constante. Por ejemplo, para los caracteres sencillos (de 1 byte de ancho) este tipo es int, que es distinto del tipo de caracteres del flujo (char en este caso).
-
Igualdad de caracteres: Podría suceder que en un tipo definido por el usuario la igualdad de caracteres estuviese definida de forma no estándar (algo distinto a una mera identidad de bits). Esta condición también tendría que ser conocida.
-
Carácter blanco ("Whitespace"): Determinadas operaciones de E/S descartan ciertos caracteres denominados "whitespaces". Además del espacio ASCII 20h, existen otros ( 5.3.3b). En el caso de tipos definidos por el usuario deben estar especificados.
Para describir estas peculiaridades se utiliza una clase genérica traits,
que es utilizada a su vez
como uno de los parámetros de las clases genéricas que encapsulan los algoritmos de E/S. Este argumento
aparece como class traits [1]. Por ejemplo, la clase basic_ios tiene la siguiente forma:
template <class charT, class traits> class basic_ios;
Para facilitar las cosas la Librería Estándar hace que estos argumentos de las plantillas tengan un valor por defecto. En concreto la definición de la clase anterior adopta realmente la forma:
template <class charT, class traits = char_traits<charT> > class basic_ios;
Podemos ver que el valor por defecto es la clase char_traits, que encapsula las peculiaridades del flujo y responde a la siguiente declaración:
template <class charT> class char_traits;
Para facilitar aún más las cosas, la Librería Estándar proporciona dos especialidades de esta plantilla para los tipos char y wchar_t, de forma que es muy raro que el usuario tenga que habérselas directamente con esta clase, a no ser que "invente" su propio tipo de caracteres y desee utilizar la jerarquía iostream para realizar operaciones de E/S con ellos.
§4 Clases de flujo
Esta
jerarquía de clases de flujo ("stream classes") sirven a la capa de formato, y
responden al esquema indicado en la figura.
Señalar que a excepción de la raíz ios_base, las demás son clases genéricas que dependen de dos argumentos. |
|
§4.1 ios_base ( 5.3.2a) | |
Actúa como superclase de la que derivan todas las demás en la jerarquía. 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, de forma que no sea necesaria ninguna especificación posterior. Por ejemplo:
Esta superclase también define diversos tipos utilizados por toda la jerarquía, como indicadores de formato, bits de estado, formas de apertura, clase para manejo de excepciones, 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.) |
|
§4.2 basic_ios ( 5.3.2b) | |
template <class charT, class traits = char_traits<charT> > class basic_ios; | |
Deriva de la superclase ios_base, y sirve como clase-base virtual para las clases basic_istream; basic_ostream y las clases derivadas de ellas. Por ejemplo, basic_iostream. Una de sus funciones es controlar la comunicación con el buffer intermedio; un objeto asociado derivado de la clase basic_streambuf , que es el que en realidad controla las operaciones de E/S, ya que los objetos derivados de las clases iostreams delegan en él estas funciones (mas propiamente diríamos que las realizan a través de los métodos del objeto streambuf). Esta clase es una plantilla que acepta dos argumentos: el tipo de carácter manejado por el flujo y las características de estos caracteres. El tipo puede ser: simple (char); ancho (wchar_t) o cualquier otro definido por el usuario. El constructor requiere que se le pase un puntero al objeto streambuf que se utilizará como buffer de flujo, ya que sin este la clase no tiene donde almacenar los datos que entran y salen. La clase alberga también información de estado que refleja la integridad del buffer. Dispone de cuatro métodos públicos que devuelven un bool con información de estado: bad(); good(); eof() y fail(). Además merecen destacarse otros dos métodos públicos en razón del uso que hacen de la sobrecarga de operadores: operator void*() const y bool operator!() const, que se utilizan para devolver información de estado mediante una sintaxis única. El primero devuelve un puntero nulo en caso de fallo o no-nulo en caso de éxito. El segundo devuelve fail(). |
|
§4.3 basic_istream ( 5.3.2c) | |
template <class charT, class traits = char_traits<charT> > class basic_istream; | |
Deriva directamente de basic_ios. Contiene métodos que ayudan a leer e interpretar flujos de entrada controlados por un streambuf. Existen dos grupos de estos métodos, que permiten leer el flujo de forma formateada y no formateada. Los primeras se denominan extractores, aunque ambas obtienen o "extraen" caracteres de un streambuf . |
|
§4.4 basic_ostream ( 5.3.2d) | |
template <class charT, class traits = char_traits<charT> > class basic_ostream; | |
Deriva de basic_ios. Contiene dos tipos de funciones que ayudan a escribir y formatear un flujo de salida. Las denominadas insertores permiten formatear el flujo de salida. El otro grupo realiza salidas sin formateo. |
|
§4.5 basic_iostream ( 5.3.2e) | |
template <class charT, class traits = char_traits<charT> > class basic_iostream; | |
Clase derivada de basic_istream y basic_ostream. Se trata pues de una composición de las anteriores, por lo que dispone de métodos para leer y escribir flujos. |
|
§4.6 basic_istringstream | |
template <class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_istringstream; | |
Clase derivada de basic_istream. Permite leer objetos de la clase basic_string. Utiliza un objeto de tipo basic_stringbuf para controlar el almacenamiento asociado. |
|
§4.7 basic_ostringstream | |
template <class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_ostringstream; | |
Clase derivada de basic_ostream. Permite escribir objetos de la clase basic_string. Utiliza un objeto de tipo basic_stringbuf para controlar el almacenamiento asociado. |
|
§4.8 basic_stringstream | |
template <class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_stringstream; | |
Clase derivada de basic_iostream. Permite leer y escribir objetos de la clase basic_string. Utiliza un objeto de tipo basic_stringbuf para controlar el almacenamiento asociado. |
|
§4.9 basic_ifstream | |
template <class charT, class traits = char_traits<charT> > class basic_ifstream; | |
Clase derivada de basic_istream especializada en lectura de ficheros. Utiliza un objeto de tipo basic_filebuf para controlar el almacenamiento asociado. |
|
§4.10 basic_ofstream | |
template <class charT, class traits = char_traits<charT> > class basic_ofstream; | |
Esta clase es derivada de basic_ostream especializada en escritura a ficheros. Utiliza un objeto de tipo basic_filebuf para controlar el almacenamiento asociado. |
|
§4.11 basic_fstream | |
template <class charT, class traits = char_traits<charT> > class basic_fstream; | |
Clase derivada de basic_iostream. Soporta lectura y escritura de ficheros. Utiliza un objeto de tipo basic_filebuf para controlar el almacenamiento asociado. |
|
template <class charT, class traits = char_traits<charT> > class istreambuf_iterator; | |
template <class charT, class traits = char_traits<charT> > class ostreambuf_iterator; |
§5 Clases de buffer de flujo
"A stream buffer class is a class that inherits from std::streambuf. It understands only one data type: the character. Interpreting those characters is someone else’s
job". Matthew H. Austern en "The Standard Librarian: Streambufs and Streambuf
Iterators". Dr.Dobb's Journal.
Esta jerarquía de clases ("Stream buffer classes") sirven a la capa de transporte, representan al buffer de flujo, y responden al esquema indicado en la figura. Los objetos instanciados de estas clases se denominan de forma genérica streambuf; filebuf si son de flujos a fichero, y stringbuf si son de flujos a memoria. La funcionalidad de esta capa puede sintatizarse en dos palabras: almacenamiento intermedio ("buffering") y transporte. | |
§5.1 basic_streambuf ( 5.3.2f) | |
template <class charT, class traits = char_traits<charT> > class basic_streambuf; | |
Esta clase sirve como superclase abstracta de la que derivan las que controlan el transporte de los flujos de E/S con dispositivos concretos. Se ocupa de aspectos comunes a todos los flujos, considerándolos como una sucesión abstracta de caracteres a los que proporciona dos servicios genéricos: "buffering" y transporte. Esta circunstancia es justamente la que permite a la capa de formato tratar los flujos con cualquier dispositivo como secuencias de caracteres, de forma que es frecuente referirse a una E/S como una "secuencia". No soporta directamente ningún dispositivo externo; en su lugar define dos funciones virtuales: overflow() y underflow() que realizarán el transporte. Estas dos funciones son las que manejan las peculiaridades del dispositivo externo conectado en cada caso que, como tales funciones virtuales, deben ser especificadas en cada subclase de la jerarquía (por ejemplo en las subclases basic_stringbuf y basic_filebuf). Las subclases derivadas de esta, instancian objetos que controlan dos secuencias: una secuencia de caracteres de entrada ("get area") y otra, de caracteres de salida ("put area"). Existen funciones para manejar caracteres. Por ejemplo, sgetc() para leer el próximo carácter del buffer o sputc() para escribirlo. Por su parte las funciones sgetn() y sputn() leen y escriben cualquier número de caracteres. También existen funciones para manipular el puntero del buffer: seekoff() y seekpos(), que son necesarias si el buffer sirve a una rutina que acepta petición de posición (acceso aleatorio). La clase también incluye un objeto ("locale") para localismos. Las clases streambuf y wstreambuf son respectivamente especializaciones de esta plantilla para los tipos char y wchar_t . | |
§5.2 basic_stringbuf | |
template <class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_stringbuf; | |
Clase derivada de basic_streambuf. Se utiliza para E/S a memoria, en cuyo caso el buffer de flujo y el dispositivo externo coinciden. El buffer interno es dinámico, de forma que puede extenderse para alberga todos los caracteres que se escriban en él. Pueden obtenerse copias de su contenido, o hacer que una cadena de caracteres sea copiada en su interior. También tiene posibilidad de asociar la entrada o la salida con una secuencia de caracteres arbitraria, que puede ser una instancia derivada de la clase basic_string. |
|
§5.3 basic_filebuf | |
template <class charT, class traits = char_traits<charT> > class basic_filebuf; | |
Clase derivada de basic_streambuf. Permite asociar una secuencia de entrada y/o de salida con un fichero, para lo que dispone de sendas funciones open() y close() que realizan la operación de asociar/desligar la secuencia del fichero. Hereda un objeto local cuya información es utilizada para la transformación del esquema de codificación del dispositivo externo al sistema utilizado internamente. La figura adjunta muestra el esquema de funcionamiento de este buffer. |
|
template <class state> class fpos; | |
Esta clase se utiliza para especificar información relativa a posición dentro de ficheros. |
§6 Typedefs
Para utilizar estas clases genéricas es preciso instanciarlas a fin de obtener una especialización para unos tipos charT y traits determinados. Hemos señalado que para facilitar las cosas, el Estándar define un valor por defecto para este último que depende del tipo charT. Además, como los flujos son normalmente de tipos simples, char o wchar_t, el Estándar declara una serie de typedefs ( 3.2.1a) que pueden ser utilizados para especializar cualquiera de las plantillas anteriores para los dos tipos más probables. Estas declaraciones son las siguientes:
// Capa de formateo (caracteres)
typedef basic_ios <char> ios; // suplerclase abstracta
typedef basic_istream <char> istream; // E en general
typedef basic_ostream <char> ostream; // S "
typedef basic_iostream <char> iostream; // E/S "
typedef basic_ifstream <char> ifstream; // E de fichero
typedef basic_ofstream <char> ofstream; // S "
typedef basic_fstream <char> fstream; // E/S "typedef basic_istringstream <char> istringstream; // E de memoria
typedef basic_ostringstream <char> ostringstream; // S "
typedef basic_stringstream <char> stringstream; // E/S "
// (caracteres anchos)
typedef basic_ios <wchar_t> wios; // superclase abstracta
typedef basic_istream <wchar_t> wistream; // E en general
typedef basic_ostream <wchar_t> wostream; // S "
typedef basic_iostream <wchar_t> wiostream; // E/S "
typedef basic_ifstream <wchar_t> wifstream; // E de fichero
typedef basic_ofstream <wchar_t> wofstream; // S "
typedef basic_fstream <wchar_t> wfstream; // E/S "
typedef basic_istringstream <wchar_t> wistringstream; // E de memoria
typedef basic_ostringstream <wchar_t> wostringstream; // S "
typedef basic_stringstream <wchar_t> wstringstream; // E/S "
// capa de transporte (caracteres)
typedef basic_streambuf <char> streambuf; // superclase abstracta
typedef basic_filebuf <char> filebuf; // E/S de fichero
typedef basic_stringbuf <char> stringbuf; // E/S de memoria
// (caracteres anchos)
typedef basic_streambuf <wchar_t> wstreambuf;// superclase abstracta
typedef basic_filebuf <wchar_t> wfilebuf; // E/S de fichero
typedef basic_stringbuf <wchar_t> wstringbuf; // E/S de memoria
typedef fpos<char_traits <char>::state_type> streampos;
typedef fpos<char_traits <wchar_t>::state_type> wstreampos;
Estas declaraciones permiten ciertas simplificaciones. Por ejemplo: ifstream es una especialización de basic_ifstream para tipos char, de forma que las sentencias que siguen son equivalentes:
basic_ifstream<char> fileObject ("File-name");
ifstream fileObject ("File-name");
Ambas instancian un objeto fileObjec de la clase basic_ifstream, al tiempo que pasan un argumento a su constructor, lo que simultáneamente a la creación del objeto, permite abrir el fichero asociado (en modo lectura).
[1] Para saber más sobre esta clase, de la mano de una de las fuentes más autorizadas, ver "Traits: a new and useful template technique" de Nathan C. Myers www.cantrip.org, autor ya citado anteriormente al tratar de la Internacionalización en C++ ( 5.2)