Universidad de Costa Rica
Facultad de Ingeniería
Escuela de Ciencias de
Tarea Programada #1
Ambientación al IDE de C++, depuración y compilación
Profesor: Adofo Di Mare
Curso: Programación CI-1201
Alumno: Carlos Mejías Álvarez
20 de Agosto de 2007
Índice
Introducción…………………………………..3
Breve Descripción………………………….....4
Objetivos…………………………………4
Requerimientos…………………………...4
Abstracción……………………………………5
Implementación………………………………..6
Código Fuente………………………………….7
Introducción
Tras
una semana de clases, se busca con la presenta asignación, conocer más el IDE
que se utilizará durante el curso para la compilación, depuración y ejecución
de aplicaciones empleando el lenguaje C++.
Luego
de observar el manejo del IDE por parte del profesor, se procede a realizar los
mismos pasos extraclase, dando paso así a la primera Tarea Programada.
En la página web : http://www.geocities.com/cmejas126/index.html
se puede revisar la documentación generada por Doxygen, dando clic en el link
llamado “Tarea Programada
Breve Descripción
Objetivos:
× Familiarizarse con el entorno de
programación del IDE llamado Visual Studio.
× Corregir los errores de compilación
generados por el IDE al depurar el programa “Deque”.
× Realizar la documentación del programa
“Deque” utilizando el software llamado “Doxygen”
× Crear el sitio web personal en el cual se
colocarán las filminas semanales, entre otras asignaciones.
Requerimientos:
× Software para compilar programas en el
lenguaje de programación C++. Utilizado: Visual Studio.
× Software para la documentación del
programa. Utilizado: Doxygen.
× Sitio web personal para alojar diferentes
archivos solicitados por el profesor. Actual: http://www.geocities.com/cmejas126/index.html
Abstracción
Después
de la instalación de todo el software requerido, se procedió a la compilación
del programa ya mencionada “Deque.
Por medio
de la información brindada en la consola del IDE se pudo determinar de un error
en la línea 237 de la clase test_deque. El error obtenido se refería a una
referencia de “k” inexistente dentro de un iterador “for”.
Se colocó
la declaración de la variable de la siguiente manera: int k; justo la línea antes
del “for”. Esto solucionó el error. Se depuró de nuevo el programa y un nuevo
error surgió.
El nuevo
error se dio esta vez en la línea 270, y se solucionó de la misma forma que el
primer error.
Luego de
solucionar estos dos errores, se depuró el programa una vez más y se obtuvieron
4 warnings, las cuales no interfieren en la ejecución del programa.
Implementación
Modelo de la clase


Código Fuente
// test_deque.cpp (C) 2006
[email protected]
/** \file
test_deque.cpp
\brief Módulo de
prueba para la clase \c deque
Este código está
basado en el trabajo de Chuck Allison
para probar la
clase \c Stack.h
Aquí lo que se
hizo fue implementar esta clase siguiendo
el paradigma de
la biblioteca STL de C++, lo que dio por
resultado la
clase \c deque.
-
http://www.google.com/search?num=100&as_q=Simplest+Unit+Test+Allison
-
http://search.yahoo.com/search?n=100&p=Simplest+Unit+Test+Allison
\author Adolfo Di Mare
<[email protected]>
\date 2006
*/
#include "deque.h"
// Clase de prueba
using namespace
std;
// En algún manual debe estar descrito qué significa este
error
// - http://www.google.com/search?num=100&as_q=c%2B%2B+C4675+Compiler+Warning
#pragma warning( push, 1 )
// check_ok( Q ); ==> Compiler Warning
(level 1) C4675
#pragma warning( disable: 4675 ) //
check_ok( Q ); ==> Compiler Warning (level 1) C4675
#include "ADH_test.h"
// Módulo para prueba unitaria de programas
/// Clase para probar el contenedor \c deque<T>
template <class T>
class test_deque : public TestCase
{
enum {
N_REPEAT = 100,
///< Las pruebas
se hacen para contenedores de tamaño <code>[0..N_REPEAT-1]</code>
};
public:
test_deque()
{ } ///< Constructor
void test_deque_deque();
void test_empty();
void test_size();
void test_capacity();
void test_front();
void test_front_const();
void test_back();
void test_back_const();
void test_push_front();
void test_push_back();
void test_pop_front();
void test_pop_back();
void test_at();
void test_example();
/// Método base que ejecuta todas las pruebas de la clase.
bool run() {
// Estos los estoy
construyendo ahora
test_example();
// Lista de métodos a probar
test_deque_deque();
test_empty();
// test_size();
// test_capacity();
test_front();
test_front_const();
// test_back();
test_back_const();
test_push_front();
test_push_back();
test_at();
return nError() == 0;
}
};
/// \fn void deque<T>::deque( size_type N )
/// \see test_deque<T>::test_deque()
/// Datos de prueba para el constructor y destructor de la
clase
template <class T>
void test_deque<T>::test_deque_deque() {
try {
deque<T> Q(0);
fail_Msg("test_deque() ==> Es incorrecto usar contenedores
de tamaño cero (0)");
}
catch (std::length_error) {
assertTrue(true); // Esta es la forma
de decir: "la prueba sí tuvo éxito"
}
for (int i = 1;
i<N_REPEAT; ++i) {
try {
deque<T> Q(i);
assertTrue(true);
assertTrue( check_ok( Q ));
}
catch
(...) {
fail_Msg("test_deque() ==> Es correcto usar contenedores de
tamaño " + ::toString(i));
}
}
}
// Esta es la forma Doxygen de documentar métodos que
están en otro lado:
/// \fn bool deque<T>::empty() const
/// \see test_deque<T>::test_empty()
/// Datos de prueba para \c deque<T>::size()
&& \c deque<T>::capacity() && \c deque<T>::empty().
/// - La idea de esta prueba es crear \c N_REPEAT
contenedores de capacidad máxima <code> j in [1..N_REPEAT[ </code>.
/// - Para cada
una de estos contenedores, se prueban los 3 métodos variando el principio del
/// contenedor
desde el índice \c 0 hasta <code> (N_REPEAT-1) </code> del vector \c m_vec[].
/// - Es necesario metérsele al <em>Rep</em>
de la clase para implementar esta prueba.
template <class T>
void test_deque<T>::test_empty() {
deque<T>::value_type temp;
for (int i=1;
i<N_REPEAT; ++i) { // "i" es la
capacidad del contenedor de prueba
for (int j=0; j<i;
++j) { //
"j" sirve para variar el sitio "m_front" donde comienza el
contenedor
deque<T> Q(i);
Q.m_front =
j; // prueba con todos los valores válidos para
Q.m_front == j
assertTrue(Q.size() == 0);
assertTrue(Q.empty());
assertTrue(Q.capacity() == i);
for (int
k=0; k<i; ++k) { // llena de valores el contenedor
assertTrue(Q.size() == k);
assertTrue(Q.capacity() == i);
Q.push_back(temp);
assertTrue( check_ok( Q ));
assertTrue(Q.size() == k+1);
assertTrue(Q.capacity() == i);
}
}
}
}
/// \fn size_type deque<T>::size() const
/// \see test_deque<T>::test_size()
/// Datos de prueba para \c deque<T>::size() && \c deque<T>::capacity() && \c deque<T>::empty()
template <class T>
void test_deque<T>::test_size() { test_empty(); }
/// \fn size_type deque<T>::capacity() const
/// \see test_deque<T>::test_capacity()
/// Datos de prueba para \c deque<T>::size() && \c deque<T>::capacity() && \c deque<T>::empty()
template <class T>
inline void
test_deque<T>::test_capacity() { test_size(); }
/// Datos de prueba para \c deque<T>::front()
&& \c deque<T>::back().
/// - La idea de esta prueba es crear \c N_REPEAT
contenedores de capacidad máxima <code> j in [1..N_REPEAT[ </code>.
/// - Para cada
una de estos contenedores, se prueba el método \c front() variando el principio
del
/// contenedor
desde el índice \c 0 hasta <code> (N_REPEAT-1) </code> del vector \c m_vec[].
/// - Es necesario metérsele al <em>Rep</em>
de la clase para implementar esta prueba.
/// - Para verficar que el valor que está almacenado en
cada posición de \c m_vec[] esta
/// prueba no usa
contenedores de tipo \c T (el parámetro de la plantilla). El contenedor de
prueba
/// almacena
número enteros, \c deque<int>, porque de esta manera se pueden usar las
/// operaciones
aritméticas para incrementar paulatinamente el valor agregado al contenedor.
template <class T>
void test_deque<T>::test_front() {
// m_vec
// ||
// \/ <1>
<2>==m_front <6>
// +---+---+---+---+---+---+---+
// | _ | _ | 0 | 1 | 2 | 3 | _ |
// +---+---+---+---+---+---+---+
// <0> |-----------+
// m_front---+
#2 #3 #4
// m_size
== 4
for (int i=1;
i<N_REPEAT; ++i) { // "i" es la
capacidad del contenedor de prueba
for (int j=0; j<i; ++j) {
deque<int> Q(i);
Q.m_front =
j; // prueba con todos los valores válidos para
Q.m_front == j
assertTrue(Q.size() == 0);
for (int k=0; k<i;
++k) { // llena de valores el contenedor
Q.push_back(k);
assertTrue( check_ok( Q ));
assertTrue(Q.front() == 0);
assertTrue(Q.back() == k);
assertTrue(Q.size() == k+1);
}
assertTrue(Q.front() == 0);
assertTrue(Q.back() == i-1);
assertTrue(Q.size()==i);
for (int k=0; k<i;
++k) { // ahora lo vacea
assertTrue(Q.front() == k);
assertTrue(Q.back() == i-1);
assertTrue(Q.size() == i-k);
Q.pop_front();
assertTrue( check_ok( Q ));
}
assertTrue_Msg( "Q.size()=="
+ ::toString(Q.size()) , Q.empty() );
}
}
}
/// Datos de prueba para \c deque<T>::front()
const
template <class T>
void test_deque<T>::test_front_const()
{
// Sólo se asegura de que sea invocada el método
"const"
// - La lógica de front() se prueba con test_front()
const deque<T> Q_const(4);
const_cast< deque<T>&
>(Q_const).m_size = 1;
assertTrue(Q_const.front() == Q_const.front());
}
/// Datos de prueba para \c deque<T>::front() && \c deque<T>::back()
template <class T>
void test_deque<T>::test_back() { test_front(); }
/// Datos de prueba para deque<T>::back() const
template <class T>
void test_deque<T>::test_back_const() {
// Sólo se asegura de que sea invocada el método
"const"
// - La lógica de back() se prueba con test_back()
const deque<T> Q_const(4);
const_cast< deque<T>&
>(Q_const).m_size = 1;
assertTrue(Q_const.back() == Q_const.back());
}
/// Datos de prueba para \c deque<T>::push_front() y \c deque<T>::pop_front()
template <class T>
void test_deque<T>::test_push_front() {
deque<T>::value_type temp;
for (int i=1;
i<N_REPEAT; ++i) {
for (int j=0; j<i;
++j) {
deque<T> Q(i);
assertTrue(Q.size() == 0);
Q.m_front =
j; // prueba con todos los valores válidos para Q.m_front
== j
assertTrue(Q.size() == 0);
for (int k=0; k<i;
++k) { // llena de valores el contenedor
assertTrue(Q.size() == k);
Q.push_front(temp);
assertTrue( check_ok( Q ));
assertTrue(Q.size() == k+1);
}
for (; k>0; --k) { //
ahora lo vacea
assertTrue(Q.size() == k);
Q.pop_front();
assertTrue( check_ok( Q ));
assertTrue(Q.size() == k-1);
}
if (!Q.empty()) {
assertTrue_Msg( "k==" +
::toString(k) , k==0 );
assertTrue_Msg( "Q.size()=="
+ ::toString(Q.size()) , Q.empty() );
}
}
}
}
/// Datos de prueba para \c deque<T>::push_back()
y \c deque<T>::pop_back()
template <class T>
void test_deque<T>::test_push_back() {
deque<T>::value_type temp;
for (int i=1;
i<N_REPEAT; ++i) {
for (int j=0; j<i;
++j) {
deque<T> Q(i);
assertTrue(Q.size()
== 0);
Q.m_front =
j; // prueba con todos los valores válidos para
Q.m_front == j
assertTrue(Q.size() == 0);
for (int k=0; k<i;
++k) { // llena de valores el contenedor
assertTrue(Q.size() == k);
Q.push_back(temp);
assertTrue( check_ok( Q ));
assertTrue(Q.size() == k+1);
}
for (; k>0; --k) { //
ahora lo vacea
assertTrue(Q.size() == k);
Q.pop_back();
assertTrue( check_ok( Q ));
assertTrue(Q.size() == k-1);
}
if (!Q.empty()) {
assertTrue_Msg( "k==" +
::toString(k) , k==0 );
assertTrue_Msg( "Q.size()=="
+ ::toString(Q.size()) , Q.empty() );
}
}
}
}
/// Datos de prueba para \c deque<T>::pop_front()
template <class T>
inline void
test_deque<T>::test_pop_front() { test_push_front(); }
/// Datos de prueba para \c deque<T>::pop_back()
template <class T>
inline void
test_deque<T>::test_pop_back() { test_push_back(); }
/// Datos de prueba para \c deque<T>::at().
/// - Es necesario
metérsele al <em>Rep</em> de la clase para implementar esta prueba.
/// - Para verficar
que el valor que está almacenado en cada posición de \c m_vec[] esta
/// prueba no usa
contenedores de tipo \c T (el parámetro de la plantilla). El contenedor de
prueba
/// almacena
número enteros, \c deque<int>, porque de esta manera se pueden usar las
/// operaciones
aritméticas para incrementar paulatinamente el valor agregado al contenedor.
/// -
Independientemente de cuál sea el tipo \c T, la prueba siempre se hace usando
enteros para
/// poder
verificar qué es lo que está almacenado en \c Q[i].
/// - Se puede
probar con todos los valores para \c Q.capacity() en \c [0..N_REPEAT[
/// - Sin
embargo, esta prueba dura DEMASIADO pues cada vez que se levanta
/// una
excepción la máquina se traga el CPU haciendo cálculos.
/// - En lugar
de probar con todos los valores, se prueba sólo con los del vector
/// \c
Pruebe_Con_Estos[].
template <class T>
void test_deque<T>::test_at() {
deque<int> Q(4); // cualquier n>0 funciona
deque<int>::value_type
*remember = Q.m_vec; // vector viejo
try {
assertTrue(Q.capacity() < N_REPEAT*2);
try {
// "Agranda" el vector a Q para poder usar
valores "fuera de rango"
deque<int>::value_type
*Doble_Vec;
Doble_Vec = new deque<int>::value_type [ 2*N_REPEAT ];
Q.m_vec = Doble_Vec; // usa el
vectorsote de prueba
assertTrue( check_ok( Q ));
}
catch (...) {
fail_Msg("test_at()");
throw;
}
// Prueba con algunos para que la prueba no dure demasiado
int Pruebe_Con_Estos[] ={ 1,2,3,4,5, 7,9,11,12,13,
23,50, 77,N_REPEAT-2,N_REPEAT-1 };
assertTrue(77<N_REPEAT-2);
int CUANTOS = sizeof(Pruebe_Con_Estos)/sizeof(*Pruebe_Con_Estos);
int i,j,k;
for (i=0; i<CUANTOS; ++i) {
for (j=0; j<Pruebe_Con_Estos[i]; ++j) {
Q.m_front = j; // prueba con todos los valores
válidos para Q.m_front == j
Q.m_size = 0; // contenedor vacío
Q.m_capacity = Pruebe_Con_Estos[i]; // usa
Pruebe_Con_Estos[i] en lugar de "i"
// marca todos los campos del vector como valores
"fuera de rango"
for (k=0; k<2*N_REPEAT; ++k) {
Q.m_vec[k] = -1;
}
// Pone en el
contenedor los valores 1.2....N_REPEAT
for (k=0; k<Pruebe_Con_Estos[i]; ++k) {
Q.push_back(k);
}
for (k=0; k<Pruebe_Con_Estos[i]; ++k) {
assertTrue( k == Q[k] );
assertTrue( k == Q.at(k) );
}
// Euler dijo que la suma (1..N_REPEAT) se calcula por
fórmula
// 1 + 2 + ... +n-1
==\ n*(n-1) / 2
// n-1+ + 1
==/
int
suma_euler = Q.m_capacity * (Q.m_capacity - 1) / 2;
// Ahora suma los que sí están dentro del vector
deque<int>::value_type
suma_real = 0;
for (k=0; k<Pruebe_Con_Estos[i]; ++k) {
deque<int>::value_type
val = Q.front();
suma_real += val;
Q.pop_front();
}
assertTrue(suma_euler == suma_real);
// Aquí debe haber únicamente Q.m_capacity valores
diferentes de -1
int count=0;
for (k=0; k<2*N_REPEAT; ++k) {
if (Q.m_vec[k] >= 0) {
++count; // cuenta los que están en rango
}
else {
try { // estos
están fuera de rango
int bomba = Q.at(k);
fail_Msg("test_at()");
}
catch (std::out_of_range) {
//
estos son los que están "fuera de rango" de acuerdo a push_back()
assertTrue(true);
}
catch (...) {
fail_Msg("test_at()");
throw;
}
}
}
assertTrue(Q.m_capacity==count);
}
}
}
catch (...) {
// Si cae aquí es porque algo falló.
// - No hay que hacer nada porque en otro lugar ya el
// problema fue
registrado invocando fail_Msg("...");
}
// se deshace del "vectorsototote"
delete [] Q.m_vec;
Q.m_vec = remember;
// restaura Q.m_vec[]
// Ahora el destructor de "Q" se encarga de
devolver
// la memoria dinámica que está usando m_vec[]
}
/// Ejemplo de uso de la clase \c deque<T>.
template <class T>
void test_deque<T>::test_example() {
const unsigned
SIZE = 7; // Capacidad del contenedor de prueba \c
Q<T> para \c test_example()
{ // La capacidad
del contenedor debe ser definido en el momento de construcción
try { // Es incorrecto usar
contenedores de tamaño cero (0)
deque<T> Q(0); // Es incorrecto usar
contenedores de capacidad cero (0)
fail_Msg("deque<unsigned> Q(0);");
}
catch (std::logic_error&) {
assertTrue(true);
}
catch (...) {
fail_Msg("Execpción inesperada");
}
}
assertTrue(SIZE
> 0);
{ // El contenedor
está vacío y su tamaño debe ser cero (0)
deque<T>
Q(SIZE);
assertTrue(
check_ok( Q ));
assertTrue(Q.size() == 0);
try { // Es incorrecto usar
un valor del contenedor si el contenedor está vacío
Q.front();
fail_Msg("Q.front();");
}
catch (std::logic_error&) {
assertTrue(true);
}
catch (...) {
fail_Msg("Q.front();");
}
try { // Es incorrecto
eliminar un valor del contenedor si el contenedor está vacío
Q.pop_front();
fail_Msg("Q.pop_front();");
}
catch (std::logic_error&) {
assertTrue(true);
}
catch (...) {
fail_Msg("Q.pop_front();");
}
assertTrue( check_ok( Q ));
}
{ // Es incorrecto
almacenar más de Q.capacity() valores en el contenedor
deque<unsigned>
Q(SIZE);
try {
for (unsigned i = 0;
i < SIZE; ++i) {
Q.push_back(i);
}
assertTrue(true);
assertTrue( check_ok( Q ));
}
catch (...) {
fail_Msg("Execpción inesperada");
return;
}
assertTrue(Q.size() == SIZE);
assertTrue(Q.back() == SIZE-1);
try { // Ya el contenedor
está llena y no le caba nada más
Q.push_back(SIZE);
fail_Msg("Q.push_back(SIZE);");
}
catch (std::logic_error&) {
assertTrue(true);
}
catch (...) {
fail_Msg("Execpción inesperada");
}
for (unsigned i = 0;
i < SIZE; ++i) {
assertTrue(Q.front() == i); // ¿¿¿ AQUI ???
try {
Q.pop_front();
assertTrue(true);
}
catch (...) {
fail_Msg("Q.pop_front();");
return;
}
}
assertTrue(Q.size() == 0);
assertTrue( check_ok( Q ) );
try { // Ya el
contenedor está vacío y no se le puede sacar nada más
Q.pop_front();
fail_Msg("Q.pop_front();");
}
catch (std::logic_error&) {
assertTrue(true);
}
catch (...) {
fail_Msg("Q.pop_front();");
}
}
}
/// Prueba \c TestCase.
/// - Como ocurre con la mayor parte de los programas de
prueba,
/// este programa
no imprime nada, pues un programa de prueba
/// sólo debiera
imprimir un mensaje de error si alguna prueba
/// falla, en cuyo
caso el programador debería revisar la
/// rutina o
método que quebró la implementación.
/// - Lo que este programa imprime es una salida muy
simple:
///
\code
///
TestCase "class deque_test<int>":
/// OK: 8380993 ERROR: 0
///
TestCase "class deque_test<float>":
///
OK: 8380993 ERROR: 0
///
\endcode
int main_TestCase() {
test_deque<int> Test_I;
test_deque<float> Test_F;
Test_I.run();
Report( cout,
Test_I );
Test_F.run();
Report( cout,
Test_F );
return Test_I.nError() + Test_F.nError();
}
/// Programa principal que ejecuta todas las pruebas.
int main() {
return main_TestCase();
}
#pragma warning( pop ) // check_ok( Q );
==> Compiler Warning (level 1) C4675
//
EOF: test_deque.cpp
// ADH_test.h (C) 2006 [email protected]
/** \file
ADH_test.h
\brief Módulo
para prueba unitaria de programas.
\author Adolfo Di Mare
<[email protected]>
\date 2006
*/
/** \mainpage
\section sec-01 Módulo para prueba unitaria de programas
El archivo de encabezado \c ADH_test.h apoya la escritura
de módulos de prueba de
unitaria programas. El modelo que se usa para esta
implementación es
similar al expuesto en este artículo "The Simplest
Automated Unit
Test Framework That Could Possibly
Work" de Chuck Allison, que se
puede obtener aquí:
-
http://www.stickyminds.com/getfile.asp?ot=XML&id=3129&fn=XDD3129filelistfilename1%2Epdf
-
http://www.google.com/search?num=100&as_q=Simplest+Unit+Test+Allison
-
http://search.yahoo.com/search?n=100&p=Simplest+Unit+Test+Allison
- Este marco de pruebas es similar a JUnit, pues se
presume que en
el futuro los
estudiantes usarán Java como su lenguaje principal
para desarrollo de
programas (pues usan C++ sólo para adiestrarse
profundamente en
técnicas de programación).
\see http://search.yahoo.com/search?n=100&p=JUnit+Java
- Como el lenguaje C++ no tiene un mecanismo similar al de
"reflexión"
de Java, en cada
prueba fallida se registra la condición de prueba
exacta, obtenida a
través de una invocación de la macro \c TEST_THIS()
que registra el
nombre del archivo \c __FILE__ y el renglón \c __LINE__
de la prueba.
\see
http://search.yahoo.com/search?n=100&p=reflection+Java
- Para simplificar este marco de pruebas no se usa la
clase \c TestResult
que sirve para
acumular resultados de las pruebas. En su lugar, se usa
una hilera enorme,
implementada con \c std::string.
- Para simplificar esta plataforma de pruebas, no se hace
diferencia entre
un caso de prueba
de prueba existoso y uno que tiene éxito porque se
ha levantado la
excepción adecuada. Esto contrasta con JUnit, que llama
"falla"
a un caso de prueba exitoso que ha levantado una excepción. Por
eso los valor
- En JUnit la clase \c TestCase es una subclase de \c
Assert; aquí no existe
la clase \c Assert
pero para mantener una leve compatibiliidad sí se provée
una funcionalidad
similar con macros cuyo nombres comienzan con "assert".
\see
http://search.yahoo.com/search?n=100&p=JUnit+Java+Assert+Method
*/
#ifndef ADH_test_h
#define ADH_test_h ///< Evita la
inclusión múltiple
#include <string> // class
std::string
#include <sstream> // class
basic_ostringstream<T,Ch>
//#include <limits.h> //
// using namespace std;
/// Definido por la biblioteca C++ estándar.
namespace std {} // Está acá para que Doxygen lo documente
/// Escuela de Ciencias de
/// \see http:www.ecci.ucr.ac.cr
namespace ECCI { }
/// Establece el ambiente en que se realizará cada prueba.
/// - \c TestSuite<TestCase>::run() invoca los métodos
///
\c TestCase::setUp() y \c TestCase::tearDown()
/// cuando ejecuta
cada prueba.
/// - Como \c TestCase::run() es un método abstracto,
/// para facilitar
la programación lo usual es que el
/// programador no
incluya invocaciones a
/// \c
TestCase::setUp() y \c TestCase::tearDown()
/// pues es más
simple dejar que lo haga
/// \c TestSuite<TestCase>::run().
/// - Por eso, si el programador cliente quiere que cada
/// prueba se
ejecute luego de establecer el ambiente con
/// su \c
TestFixture, lo más práctico es que agregue sus
/// pruebas a una
colección de pruebas \c TestSuite.
class TestFixture {
public:
virtual void setUp()
{ } ///< Establece el ambiente de prueba
virtual void
tearDown() { } ///< Destruye el ambiente de prueba
// virtual
~TestCase() { tearDown(); } ///< Destructor
}; // TestFixture
/// NO IMPLEMENTADO ==> Colector genérico de resultados
de prueba.
/// - No está implementado para \c ADH_test.h
/// - En \c ADH_test.h se usa una hilera \c std::string
para colectar
/// todos los resultados.
\see \c TestCase::toString().
/// - C++ tiene la ventaja sobre Java porque el
programador puede
/// usar las
macros \c __FILE__ y \c __LINE__ para
registrar el
/// sitio en que
se produce un error en tiempo de ejecución.
class TestResult {
private:
TestResult(); ///< Prohibe crear objetos de tipo
\c TestResult.
};
/// NO IMPLEMENTADO ==> Clase abastracta para ejecutar
las pruebas y recolectar los resultados.
/// - No está implementado para \c ADH_test.h
/// - En \c ADH_test.h se usa una hilera \c std::string
para colectar
/// todos los
resultados. \see \c TestCase::toString().
class Test {
private: Test(); ///< Prohibe crear objetos de tipo \c Test.
public:
/// Retorna la cantidad de pruebas a realizar
virtual int
countTestCases() = 0;
/// Ejecuta la prueba y recolecta sus resultados en \c
result
virtual void run(TestResult&
result) = 0;
};
#include <typeinfo.h> // class
std::type_info ==> char* typeid().name();
/// Cada caso de prueba es una instancia derivada de esta clase
abstracta.
/// - Siempre es obligatorio implementar \c
TestCase::run().
class TestCase : public
TestFixture {
protected:
int m_pass; ///<
Cantidad de pruebas exitosas
int m_error; ///<
Cantidad de pruebas que han fallado
const char * m_name; ///< Nombre del caso de prueba
/// Hilera "enooorme" para registrar los mensajes
de pruebas fallidas, separados por \c "\n"
std::string m_errorString;
public:
TestCase(const char * name=0);
virtual ~TestCase() { } ///< Destructor
virtual bool
run() = 0; ///< Ejecuta la prueba y retorna \c
"false" si falla
bool Run() { return run(); } ///<
Sinónimo de \c run()
bool runTest()
{ return run(); } ///< Sinónimo de \c run()
int nPass() const
{ return m_pass; } ///<
Cantidad de pruebas exitosas
int nError() const { return m_error; } ///<
Cantidad de pruebas que han fallado
void recordSuccess() { ++m_pass; } ///< Registra como exitoso el resultado de una prueba
virtual void reset()
{ m_pass = m_error = 0; m_errorString = ""; } ///<
Anula los contadores de pruebas
public:
int countTestCases() const
{ return nPass()+nError(); } ///< Cantidad de pruebas realizadas
/// Obtiene el nombre de la prueba
std::string getName() const
{ return ( m_name != 0 ? m_name : typeid(*this).name());
}
void setName( const
char * name=0 ) { m_name = name; } ///< Le cambia el nombre a la prueba
/// Hilera "enooorme" para registrar los mensajes
de pruebas fallidas, separados por \c "\n"
const std::string& toString() const
{ return m_errorString; }
const std::string& errorString() const { return
toString(); } ///< Sinónimo de \c toString()
protected:
void recordError( const
std::string& lbl, const char* fname, int
lineno);
void testThis( bool
cond, const std::string& lbl, const char* fname, long lineno);
void testThis( bool
cond, const char* lbl, const
char* fname, long
lineno) {
testThis( cond, std::string(lbl), fname, lineno);
} ///< Sinónimo de \c testThis()
private:
TestCase(const TestCase&); ///<
\c "private" evita la copia de casos de prueba
TestCase& operator=(const
TestCase&); ///< \c "private" evita
la copia de casos de prueba
}; // TestCase
/// NO IMPLEMENTADO ==> Colección de pruebas.
class TestSuite : public TestCase
{ };
inline TestCase::TestCase(const char * name) : m_pass(0), m_error(0), m_name(name),
m_errorString() {
m_errorString = "";
} ///< Constructor
/** Efectúa la prueba y registra el resultado.
- Si la prueba
es exitosa sólo incrementa la cantidad de éxitos \c nPass().
- Si la prueba
falla reporta en \c toString() el
hecho.
- La prueba es
\c "cond".
- Los valores \c
"fname" y \c "lineno" indican el archivo y el renglón
en donde se
ejecuta la prueba.
- Usualmente los
valores de \c "fname" y \c "lineno" se obtienen con
las macros
globales \c "__FILE__" y \c "__LINE__".
Este método es
invocado usando la macro homónima \c TEST_THIS() */
inline void TestCase::testThis( bool cond, const
std::string& lbl, const char* fname, long
lineno) {
if (cond) {
recordSuccess();
}
else {
recordError(lbl, fname, lineno);
}
}
/** Registra la falla de la prueba y luego lo acumula en
la hilera de fallas \c toString().
- Los valores \c
"fname" y \c "lineno" indican el archivo y el renglón
en donde se
ejecuta la prueba.
- Usualmente los
valores de \c "fname" y \c "lineno" se obtienen con
las macros
globales \c "__FILE__" y \c "__LINE__".
Este método es
invocado usando la macro \c TEST_ERROR() */
inline void TestCase::recordError( const std::string& lbl, const
char* fname, int
lineno) {
//
typedef basic_ostringstream<char> ostringstream;
std::basic_ostringstream<char>
ost; // ostringstream ost;
ost << "=\\_error: " << lbl << " \n=/ (" << lineno << ") " << fname << "\n";
m_errorString
+= ost.str();
m_error++;
}
/// [ADH_test] Macros propios de \c ADH_test.h
#define TEST_ADH_test()
#undef TEST_ADH_test
/// [ADH_test] Efectúa la prueba \c cond y registra el
resultado.
/// - Si la prueba \c cond tiene éxito invoca el método
/// \c
TestCase::recordSuccess()
/// - Si la prueba \c cond falla invoca el método
/// \c
TestCase::recordFail()
/// - Esta es una una macro que invoca el método
///
\c TestCase::testThis()
///
\code
///
TEST_THIS("0 != 1"); //
Siempre tiene éxito
/// testThis(0 !=
1, "0 != 1", __FILE__, __LINE__ ); // Siempre tiene éxito
///
\endcode
#define TEST_THIS(cond) testThis( cond, #cond, __FILE__, __LINE__ )
/// [ADH_test] Macro similar a \c TEST_THIS() que usa el
mensaje \c msg en caso de falla.
/// - El mensaje \c msg debe ser una hilera literal o un
objeto que pueda
///
convertirse en <code>const char *</code>
#define TEST_THIS_Msg(msg, cond) testThis( cond, msg, __FILE__,
__LINE__ )
/// [ADH_test] Registra como "error" el
resultado de una prueba.
/// - El programador cliente es quien determinó que la
prueba
/// falló y por
eso quiere registrar ese hecho.
/// - Está implementado comun una macro que invoca
/// el método \c
TestCase::recordError()
///
/// \code
/// if (22==33) {
///
TEST_ERROR("22 != 33"); // Registra que la prueba falló
///
}
///
\endcode
#define TEST_ERROR(str) recordError( str, __FILE__, __LINE__ )
/// [ADH_test] Registra como "exitoso" el
resultado de una prueba.
/// - El programador cliente es quien determinó que la
prueba
/// fue exitosa y
por eso quiere registrar ese hecho.
/// - Generalmente se usa después de atrapar una excepción
que
/// se supone
debió ser lanzada como resultado de la prueba.
/// - Está implementado comun una macro que invoca
/// el método \c
TestCase::recordSuccess()
///
/// \code
/// queue<T> Q(SIZE); //
la cola "Q" está vacía ==> no tiene "front()"
/// try {
///
Q.front(); // Es incorrecto usar
un valor de la cola si la cola está vacía
///
TEST_ERROR("Q.front();"); // Fallaría si "front()"
no levanta la excepción
/// }
/// catch
(std::logic_error&) {
///
TEST_SUCCESS(); // Esto es lo correcto pues "front()" levanta
"logic_error" para unca cola vacía
///
}
///
\endcode
#define TEST_SUCCESS() recordSuccess()
/// [ADH_test] Efectúa la prueba para determinar si <code>expected
== actual</code>.
/// - Si la prueba tiene éxito invoca el método
/// \c
TestCase::recordSuccess()
/// - Si la prueba falla invoca el método
/// \c
TestCase::recordFail()
/// - Esta es una una macro que invoca el método
///
\c TestCase::testThis()
#define TEST_EQUAL(expected, actual) \
testThis(
(expected) == (actual), #expected " == "
#actual, __FILE__, __LINE__ )
/// [ADH_test] Efectúa la prueba para determinar si <code>expected
== actual</code>.
/// - Si la prueba tiene éxito invoca el método
/// \c
TestCase::recordSuccess()
/// - Si la prueba falla invoca el método
/// \c TestCase::recordFail()
/// - Esta es una una macro que invoca el método
/// \c
TestCase::testThis()
/// - Registra el mensaje \c MSG si la prueba falla.
#define TEST_EQUAL_Msg(MSG, expected, actual) \
testThis(
(expected) == (actual), MSG, __FILE__, __LINE__ )
/// Operación de grabado que permite reportar el resultado
de la prueba \c test.
/// - Graba en \c COUT el nombre, cantidad de éxitos y
cantidad de errores.
std::ostream & operator
<< (std::ostream& COUT, const
TestCase& test) {
COUT <<
"TestCase \"" << test.getName() << "\":\n"
<<
"\tOK: " << test.nPass()
<<
"\tERROR: " << test.nError() << "\n";
return COUT;
}
/// Graba en \c "COUT" el resultado de la prueba
\c test.
/// - Retorna la cantidad total de errores \c test.nError().
/// - Además, graba la cantidad de pruebas exitosas y
erróneas.
/// - Si hubo pruebas no exitosas, graba los mensajes de
error correspondientes.
inline long Report(
std::ostream& COUT, const TestCase
&test ) {
COUT <<
test;
if (test.nError() != 0) {
COUT
<< test.errorString();
}
return test.nError();
}
// ¿¿¿ TestSuite ???
//inline bool check_ok<int>(
const int & ) { return true; }
//template<class unsigned>
inline bool check_ok( const unsigned & ) { return true; }
//template<class int> inline
bool check_ok( const long & ) {
return true; }
//template<class int> inline
bool check_ok( const flota & ) { return true; }
//template<class int> inline
bool check_ok( const double & ) {
return true; }
template <class T> bool check_ok( const
T& ); ///< Declaración genérica para \c
check_ok()
inline bool check_ok( const signed char & )
{ return true;
} ///< \c
check_ok<>()==true por defecto (por si
el programador no lo ha implementado)
inline bool check_ok( const unsigned char & ) { return
true; } ///< \c check_ok<>()==true
por defecto (por si el programador no lo ha implementado)
inline bool check_ok( const signed int & )
{ return true;
} ///< \c
check_ok<>()==true por defecto (por si
el programador no lo ha implementado)
inline bool check_ok( const unsigned int& ) { return true; } ///< \c check_ok<>()==true por defecto (por si el programador no lo ha
implementado)
inline bool check_ok( const signed long & )
{ return true;
} ///< \c
check_ok<>()==true por defecto (por si
el programador no lo ha implementado)
inline bool check_ok( const unsigned long & ) { return
true; } ///< \c check_ok<>()==true
por defecto (por si el programador no lo ha implementado)
inline bool check_ok( const float
& ) { return true; } ///< \c check_ok<>()==true por defecto (por si el programador no lo ha
implementado)
inline bool check_ok( const double
& ) { return
true; } ///< \c check_ok<>()==true
por defecto (por si el programador no lo ha implementado)
inline bool check_ok( const long double &
) { return true;
} ///< \c
check_ok<>()==true por defecto (por si
el programador no lo ha implementado)
/// Retorna una hilera que contiene el valor de \c val.
/// - \c toString() with standard C++
template <class T>
std::string toString( const
T & val ) {
//
typedef basic_ostringstream<char> ostringstream;
std::basic_ostringstream<char>
temp; // ostringstream temp;
temp <<
val;
return temp.str( );
}
/// [CppUnit] Macros propios de \c
CppUnit http://cppunit.sourceforge.net/doc/lastest
#define CPPUNIT_ADH_test()
#undef CPPUNIT_ADH_test
/// [CppUnit] Assertions that a
condition is true.
/// \see
http://cppunit.sourceforge.net/doc/lastest/group___assertions.html#ga0
#define CPPUNIT_ASSERT(condition) TEST_THIS(condition)
/// [CppUnit] Assertion with a user
specified message.
/// \see
http://cppunit.sourceforge.net/doc/lastest/group___assertions.html#ga1
#define CPPUNIT_ASSERT_MESSAGE(message, condition)
CPPUNIT_ASSERT(condition)
/// [CppUnit] Fails with the
specified message.
/// \see
http://cppunit.sourceforge.net/doc/lastest/group___assertions.html#ga2
#define CPPUNIT_FAIL(message) TEST_ERROR(message)
/// [CppUnit] Asserts that two values
are equals.
/// \see
http://cppunit.sourceforge.net/doc/lastest/group___assertions.html#ga3
#define CPPUNIT_ASSERT_EQUAL(expected, actual) TEST_EQUAL(expected,
actual)
/// [CppUnit] Asserts that two values
are equals, provides additional messafe on failure.
/// \see
http://cppunit.sourceforge.net/doc/lastest/group___assertions.html#ga4
#define CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expected, actual) \
TEST_EQUAL_Msg(message, expected, actual)
/// [CppUnit] Macro for primitive
value comparisons.
/// \see
http://cppunit.sourceforge.net/doc/lastest/group___assertions.html#ga5
#define CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, delta) \
assertEquals_Delta(expected, actual, delta)
/// [CppUnit] Asserts that the given
expression throws an exception of the specified type.
/// \see http://cppunit.sourceforge.net/doc/lastest/group___assertions.html#ga6
#define CPPUNIT_ASSERT_THROW(expression, ExceptionType) \
do { \
bool cpputExceptionThrown_ = false; \
try {
\
expression;
\
} catch ( const
ExceptionType & ) { \
cpputExceptionThrown_ = true; \
}
\
\
if ( cpputExceptionThrown_ ) { \
break; \
} \
TEST_ERROR( \
"Expected exception: " #ExceptionType
) \
} while ( false )
/// [CppUnit] Asserts that the given
expression does not throw any exceptions.
/// \see
http://cppunit.sourceforge.net/doc/lastest/group___assertions.html#ga7
#define CPPUNIT_ASSERT_NO_THROW(expression) \
do {
\
try { \
expression; \
} catch ( ... ) { \
TEST_ERROR("Unexpected exception
caught"); \
}
\
} while ( false )
/// [CppUnit] Asserts that an
assertion fail.
/// \see
http://cppunit.sourceforge.net/doc/lastest/group___assertions.html#ga8
#define CPPUNIT_ASSERT_ASSERTION_FAIL(assertion) \
CPPUNIT_ASSERT_THROW( assertion, CPPUNIT_NS::Exception )
/// [CppUnit] Asserts that an
assertion pass.
/// \see
http://cppunit.sourceforge.net/doc/lastest/group___assertions.html#ga9
#define CPPUNIT_ASSERT_ASSERTION_PASS(assertion) \
CPPUNIT_ASSERT_NO_THROW( assertion )
// /// Concatena la hilera \c THIS
con la hilera \c THAT
// #define ADH_test_CAT( THIS, THAT )
(std::string(THIS) + std::string(THAT).c_str()
#define JUnit_ADH_test()
#undef JUnit_ADH_test
/// [JUnit] Macros propios de \c JUnit
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html
///
Asserts that two objects are equal.
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertEquals(java.lang.Object,%20java.lang.Object)
#define assertEquals(
EXPECTED, ACTUAL ) TEST_EQUAL(EXPECTED, ACTUAL)
///
Asserts that two objects are equal (with message).
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertEquals(java.lang.String,%20java.lang.Object,%20java.lang.Object)
#define assertEquals_Msg( MSG, EXPECTED, ACTUAL ) TEST_EQUAL_Msg(MSG,
EXPECTED, ACTUAL)
/// [JUnit] Asserts that a condition
is true.
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertTrue(boolean)
#define assertTrue(
CONDITION ) testThis( CONDITION, #CONDITION, __FILE__, __LINE__ )
/// [JUnit] Asserts that a condition
is true (with message).
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertTrue(java.lang.String,%20boolean)
#define assertTrue_Msg( MSG,
CONDITION ) testThis( CONDITION,
MSG, __FILE__, __LINE__ )
/// [JUnit] Asserts that a condition
is false.
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertFalse(boolean)
#define assertFalse(
CONDITION ) testThis( !(CONDITION), "!("
#CONDITION ")", __FILE__, __LINE__
)
/// [JUnit] Asserts that a condition
is false (with message).
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertFalse(java.lang.String,%20boolean)
#define assertFalse_Msg( MSG, CONDITION ) testThis(
!(CONDITION), MSG, __FILE__, __LINE__ )
#include <math.h> // fabs()
/// [JUnit] Asserts that two doubles
are equal concerning a delta.
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertEquals(double,%20double,%20double)
#define assertEquals_Delta(EXPECTED, ACTUAL, DELTA ) \
testThis(
fabs( double(EXPECTED) - double(ACTUAL)
) < double(DELTA), \
"|"
#EXPECTED "-" #ACTUAL "| < " #DELTA, __FILE__, __LINE__ )
/// [JUnit] Asserts that two doubles
are equal concerning a delta (with message).
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertEquals(java.lang.String,%20double,%20double,%20double)
#define assertEquals_Delta_Msg( MSG, EXPECTED, ACTUAL, DELTA ) \
testThis(
fabs( double(EXPECTED) - double(ACTUAL)
) < double(DELTA), \
MSG,
__FILE__, __LINE__ )
/// [JUnit] Asserts that an object is
null.
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertNull(java.lang.Object)
#define assertNull(OBJECT)
testThis( 0==&(OBJECT), "assertNull(" #OBJECT ")",
__FILE__, __LINE__ )
/// [JUnit] Asserts that an object
isn't null.
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertNotNull(java.lang.Object)
#define assertNotNull(OBJECT) testThis( 0!=&(OBJECT), "assertNotNull(" #OBJECT ")", __FILE__, __LINE__ )
/// [JUnit] Asserts that two objects
refer to the same object.
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertSame(java.lang.Object,%20java.lang.Object)
#define assertSame(THIS, THAT) testThis( &(THIS)==&(THAT), "assertSame(" #THIS ",
" #THAT ")", __FILE__,
__LINE__ )
///
Asserts that two objects do not refer to the same object.
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#assertNotSame(java.lang.Object,%20java.lang.Object)
#define assertNotSame(THIS, THAT) testThis(
&(THIS)!=&(THAT), "assertNotSame("
#THIS ", " #THAT ")", __FILE__, __LINE__ )
/// [JUnit] Fails a test with no
message.
/// \see http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#fail()
#define fail( ) TEST_ERROR("ERROR")
/// [JUnit] Fails a test with the
given message.
/// \see
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html#fail(java.lang.String)
#define fail_Msg( MSG ) TEST_ERROR(MSG)
#endif //
ADH_test_h
//
EOF: ADH_test.h
// deque.h (C) 2006 [email protected]
/** \file deque.h
\brief Vector
extendible a ambos lados, almacenado en un vector circular.
\author Adolfo Di Mare <[email protected]>
\date 2006
*/
#include <exception> // std::length_error && std::bad_alloc
#include <iostream> // cin &&
cout
// using namespace std;
/// Definido por la biblioteca C++ estándar.
namespace std {} // Está acá para que Doxygen lo documente
template <class T> class test_deque;
/** Vector extendible a ambos lados, almacenado en un
vector circular.
- Es posible
agregar valores al principio o al final del vector extendible.
- El tamaño del
vector extendible no puede cambiar, pues se establece en el
constructor.
- Why English names??? ==>
http://www.di-mare.com/adolfo/binder/c01.htm#sc04
*/
template <class T>
class deque {
public:
typedef typename
T value_type; ///<
Nombre estándar del objeto contenido
typedef typename T
* pointer; ///< Nombre
estándar del puntero al objeto contenido
typedef typename T
& reference; ///< Nombre
estándar de la referencia al objeto contenido
typedef typename const value_type const_value_type; ///< Nombre estándar del objeto contenido \c const
typedef typename const T *
const_pointer; ///< Nombre estándar del
puntero al objeto contenido \c const
typedef typename const T &
const_reference; ///< Nombre estándar de la
referencia al objeto contenido \c const
typedef typename unsigned size_type;
///< Nombre estándar del tipo retornado por
\c size()
/// Constructor: Vector extendible con capacidad para
almacenar \c "N" valores
deque( size_type N );
~deque() { if (m_vec != 0) { delete
[] m_vec; } } ///< Destructor
/// Retorna \c "true" si el vector extendible
está vacía
bool empty() const { return (size() == 0); }
/// Cantidad de valores almacenados en el vector
extendible.
size_type size()
const { return
m_size; }
/// Cantidad máxima de valores que se pueden almacenar en
el vector extendible.
size_type capacity() const
{ return m_capacity; }
/// Retorna una referencia al primer valor del vector
extendible.
/// - El primero valor es el que está "al frente"
en el vector extendible.
/// \pre \c !empty()
/// \see test_deque<T>::test_front()
value_type& front() { return
at_check(m_front); }
/// Retorna una referencia constante al primer valor del
vector extendible.
/// - El primero valor es el que está "al frente"
en el vector extendible.
/// \pre \c !empty()
/// \see test_deque<T>::test_front_const()
const value_type& front() const { return const_cast<deque*>(this)->front();
}
// const_cast<deque*>(this) hace que
"this" ya NO sea un puntero "const".
/// Retorna una referencia al último valor del vector
extendible.
/// - El úlimo valor es el que está "al final" en
el vector extendible.
/// \pre \c !empty()
/// \see test_deque<T>::test_back()
value_type&
back() { return at_check(m_front+m_size-1); }
/// Retorna una referencia constante al último valor del
vector extendible.
/// - El úlimo valor es el que está "al final" en
el vector extendible.
/// \pre \c !empty()
/// \see test_deque<T>::test_back_const()
const value_type& back() const
{ return const_cast<deque*>(this)->back(); }
/// Acceso al \c "i"-ésimo valor almacenado en
el contenedor.
/// - No levanta ninguna excepción si \c "i" está
fuera del rango válido.
value_type&
operator[](size_type i) {
size_type i_real = m_front + i;
i_real =
( i_real < m_capacity ? i_real : i_real - m_capacity );
return m_vec[ i_real ]; // uso "+" y "-" que es más eficiente
que "%"
}
/// Acceso al \c "i"-ésimo valor almacenado en el
contenedor ( \c const ).
/// - No levanta ninguna excepción si \c "i" está
fuera del rango válido.
value_type& operator[](size_type
i) const { return
const_cast<deque*>(this)->operator[](i);
}
/// Acceso al \c "i"-ésimo valor almacenado en
el contenedor.
/// - Levanta la excepción \c std::out_of_range si \c
"i" está fuera del rango válido.
value_type& at(size_type i) { return
at_check(m_front+i); }
/// Acceso al \c "i"-ésimo valor almacenado en
el contenedor ( \c const ).
/// - Levanta la excepción \c std::out_of_range si \c
"i" está fuera del rango válido.
value_type& at(size_type i) const { return const_cast<deque*>(this)->at(i);
}
void push_front( const
value_type& val );
void push_back(
const value_type& val );
void pop_front();
void pop_back();
template <class
T> friend std::istream& operator>>( std::istream& CIN, deque<T>& Q);
template <class
T> friend std::ostream& operator<<( std::ostream& COUT, const deque<T>& Q);
private:
bool ok() const;
inline friend bool check_ok( const
deque<T>& Q) { return Q.ok(); }
value_type& at_check(size_type i);
explicit deque(const deque&); ///<
\c "private" evita que el objeto sea copiado
template <class
T> friend class
test_deque; ///< Datos de prueba para la clase
private:
unsigned
m_front; ///< Indice del primero del vector extendible.
unsigned
m_size; ///< Cantidad de valores almacenados en el vector
extendible (puede ser cero).
unsigned
m_capacity; ///< Cantidad máxima de valores
que pueden estar almacenados en el vector extendible.
value_type *
m_vec; ///<
Bloque de memoria para para almacenar los \c m_capacity valores que caben en el
vector extendible.
};
template <class T>
deque<T>::deque( size_type N ) : m_front(0),
m_size(0), m_capacity(0), m_vec(0) {
if (N == 0) {
throw std::length_error("deque<T>::deque(0)");
}
else {
try {
m_capacity = N;
m_vec
= new value_type [ m_capacity ];
}
catch (std::bad_alloc) {
throw; // new[] tira
std::bad_alloc si ya no hay memoria
}
}
}
/** Acceso al \c "i"-ésimo valor del vector
extendible.
- Si
<code>(i=>m_capacity)</code> retorna el valor \c
m_vec[i-m_capacity].
- Verifica que
el valor de \c "i" esté en el rango válido.
- Está
implementado de manera que el índice de \c at(m_front+m_size-1)
siempre esté
en el rango válido.
- Funciona como
si los índices que están a partir de \c m_capacity estuvieran en un
vector, a la par de \c m_vec[].
- Rango válido para \c "i": <code>[[ m_front ==>
m_front + m_size - 1 ]]</code>.
- Levanta la excepción \c
std::out_of_range si \c "i" está fuera del rango válido.
\code
m_vec[] m_capacity==7
||
\/ m_front==<5> <6>
+---+---+---+---+---+---+---+ -
at_check(<5>) es m_vec[5]
| 5 | 6 | _ | _ | _ | 3 | 4 | -
at_check(<6>) es m_vec[6]
+---+---+---+---+---+---+---+ -
at_check(<7>) es m_vec[0]
+-------+
|-------+ - at_check(<8>)
es m_vec[1]
| #3
#4 m_front---+ #2 |
| | - Sólo estos accesos son válidos
+-<-<-----------------------<-<-+ - los demás levantan \c std::out_of_range
m_size == 4
\endcode
*/
template <class T>
T& deque<T>::at_check(size_type i) {
// uso "+" y "-" que es más eficiente
que "%"
size_type i_real = ( i < m_capacity ? i : i -
m_capacity );
if ( (m_front <= i) && (i < m_front +
m_size) ) {
return m_vec[i_real];
}
else {
throw std::out_of_range("deque<T>::at()");
}
}
/// Agrega una copia de \c "val" al final del
vector extendible.
/// \pre
/// <code>size() <
m_capacity</code>
/// \see test_deque<T>::test_push_back()
template <class T>
void deque<T>::push_back(const
value_type& val) {
if (m_size < m_capacity) {
size_type
i_real = m_front + m_size;
i_real = (i_real
< m_capacity ? i_real : i_real - m_capacity);
m_vec[i_real]
= val;
m_size++;
}
else {
throw std::out_of_range("deque<T>::push_back()");
}
}
/// Agrega una copia de \c "val" al principio
del vector extendible.
/// \pre
/// <code>size() <
m_capacity</code>
/// \see test_deque<T>::test_push_front()
template <class T>
void deque<T>::push_front(const value_type& val) {
if (m_size < m_capacity) {
m_front = ( m_front>0 ? m_front-1 : m_capacity-1 );
m_vec[m_front]
= val;
m_size++;
}
else {
throw std::out_of_range("deque<T>::push_front()");
}
}
/// Saca del vector extendible al valor que está al
frente.
/// \pre
/// <code>!empty()</code>
/// \see test_deque<T>::test_pop_front()
template <class T>
inline void
deque<T>::pop_front() {
if (m_size > 0) {
// unsigned temp =
m_front;
m_front++;
m_size--;
if (m_front >= m_capacity) {
m_front = 0;
}
// return
m_vec[temp];
}
else {
throw std::out_of_range("deque<T>::pop_front()");
}
}
/// Saca del vector extendible al valor que está al
frente.
/// \pre
/// <code>!empty()</code>
/// \see test_deque<T>::test_pop_back()
template <class T>
inline void deque<T>::pop_back()
{
if (m_size > 0) {
m_size--;
}
else {
throw std::out_of_range("deque<T>::pop_front()");
}
}
#if 0
/// Retorna \c
"true" si el vector extendible está llena
template
<class T>
inline bool deque<T>::full() const {
return (m_size == m_ capacity);
}
#endif
/** Verifica la invariante de la clase \c deque<T>.
- Regresa \c
"true" si \c "*this" contiene un valor correcto.
- Podría
retornar \c "true" aún cuando \c "*this" tenga su valor
corrupto.
- Podría no
retornar si \c "*this" tiene su valor corrupto.
-
http://www.di-mare.com/adolfo/binder/c04.htm#sc11
\see
deque<T>::ok( )
\par Rep:
Diagrama de la clase
\code
+-------------------------------+ +-----------------------------------+
| m_vec[] |
| m_vec[] m_capacity==7 |
| || | | || |
| \/ <1> <2>==m_front <6>
| | \/ m_front==<5> <6> |
| +---+---+---+---+---+---+---+
| | +---+---+---+---+---+---+---+ |
| | _ | _ | 3 |
9 | 5 | 6 | _ | | | | 5 | 6 | _ | _ | _
| 3 | 9 | |
|
+---+---+---+---+---+---+---+ | |
+---+---+---+---+---+---+---+ |
| <0>
|-----------+ | | +-------+ |-------+ |
|
m_front---+ #2 #3
#4 | | | #3
#4 m_front---+ #2 |
|
| | | | | |
| m_size == 4 | | +-<-<-----------------------<-<-+
|
+-------------------------------+
+-----------------------------------+
\endcode
- <em>Rep</em>:
http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep
\par Rep: Descripción en
palabras
- Los valores
del vector extendible están almacenados en memoria dinámica,
consecutivamente, en el vector \c "m_vec[]" cuyo tamaño no
puede
cambiar pues
queda definido cómo \c "deque<T>::m_capacity" cuando el
objeto es
construido.
- El primer
valor del vector extendible siempre está almacenado en
\c "m_vec[m_front]".
- El último valor del vector
extendible siempre está almacenado en
\c "m_vec[ (m_front+m_size-1) %
m_capacity ]".
- En el diagrama se muestran
2 formas en que puede estar almacenado el
valor de una
cola <code>Q = [3,9,5,6]</code> con \c "3" al frente y
\c
"6" al final.
- En esta
implementación del vector extendible se usa un vector circular en donde
los valores
están almacenados desde el índice \c "m_front" de \c
"m_vec[]".
- Como siempre
el "frente" del vector extendible debe estar "antes" que el
"final",
cuando el
valor de \c "m_front" no es \c "0", porque no apunta al
principio del
vector, lo que ocurre es que el vector extendible está almacenado
llegando al
final del vector y luego volviendo al principio
(<em>wrap-around</em>).
- La otra forma
de almacenar el vector extendible en un vector circular es usar
los campos \c
"m_front" y \c "m_back", pero esta implementación
tiene el problema
de que es necesario desperdiciar uno de los
campos del
vector \c "m_vec[]" pues de otra manera no es posible
determinar si
el vector extendible está lleno o vacío.
*/
template <class T>
bool check_ok( const
deque<T>& Q);
/** Verifica la invariante de la clase.
- Implementación
para \c check_ok( const deque<T>& Q ).
\see check_ok( const
deque<T>& Q)
\remark
-
Desafortunadamente no fue posible lograr que el compilador aceptara
una
implementación directa para \c check_ok( const deque<T>& Q )
- El ligador
(<em>linker</em>) emite el error LNK2019. El truco para
lidiar con
este problema (<em>work arount</em>) es implementar \c ok()
- Es mejor que
\c check_ok() sea una función para evitarle al programador
usuario la obligación de implementar el
método \c ok()
-
http://www.di-mare.com/adolfo/binder/c04.htm#sc11
-
http://www.google.com/search?num=100&as_q=LNK2019+template+c%2B%2B
-
http://search.yahoo.com/search?n=100&p=LNK2019+template+c%2B%2B
*/
template <class T>
bool deque<T>::ok() const {
if (this == 0) {
/// - Invariante: ningún objeto puede estar almacenado en
la posición nula.
return false;
}
if ( ! (m_capacity > 0) ) {
/// - Invariante: La capacidad \c m_capacity debe ser
mayor que cero.
return false;
}
if ( ! (m_front < m_capacity) ) {
/// - Invariante: El índice \c m_front no puede exceder \c
m_capacity.
return false;
}
if ( ! (m_size <= m_capacity) ) {
///
- Invariante: El índice \c m_size no puede exceder \c m_capacity.
return false;
}
if ( ! (size() <= m_capacity) ) {
/// - Invariante: La cantidad de valores almanenados \c
size() no puede exceder \c m_capacity.
return false;
}
if ( ! (m_vec != 0) ) {
/// - Invariante: El vector \c m_vec[] debe existir en
memoria dinámica.
return false;
}
// Declaración previa del verificador del objeto contenido
// - Con este se le quita trabajo al programador cliente de
la clase
// pues deberá
implementar \c check_ok( const T& ) sólo en el caso
// de que utilice
\c check_ok() para \c deque<T> .
// - En otras palabras: el programador cliente está
obligado a implementar
// este método sólo
si invoca \c check_ok( const deque<T>& ).
// - Por eso es preferible que \c check_ok() sea una
función y no un método.
bool check_ok( const
deque<T>::value_type & );
for (unsigned i=0;
i<m_capacity; ++i) {
if ( ! check_ok( m_vec[i] ) ) {
/// - Invariante: Todos los valores almacenados en el
contenedor
/// deben cumplir
con su propia invariante
return false;
}
}
return true;
/*
NOTA: VC++ v7 .net
La documentación del compilador
pide que check_ok( const deque<T>& ) sea declarado
dentro de la
plantilla deque<T> como una función "friend" de la siguiente
forma:
template <class T> friend bool
check_ok( const deque<T>& Q);
Desafortunadamente, el
compilador produce un error interno si check_ok() es una
función. Por
eso, en esta implementación se usa el método deque<T>::ok(), el que
sí es aceptado
por el compilador. Para evitar una pequeña degradación en la
eficiencia, la
función check_ok() queda definida dentro de la clase como una
función
"inline", que el compilador compila sin dar problemas.
A primera vista
parece inocuo exigir que quien use deque<T> esté obligado a
implementar el
método T::ok(), pero en la práctica eso es contraproducente por
2 razones:
- Algunos
"tipos" en C++ no son clases, como "int" o
"float", por lo que no
es posible
definirles su método ok().
- Para un
programador cliente es incómodo agregar un método que no piensa usar
Por eso, si el
programador cliente invoca check_ok( const deque<T>& ) deberá
implementar
check_ok( const T& ), pero de otra forma ni siquiera tiene que
enterarse de
que check_ok() existe.
*/
}
//
EOF: deque.h