Universidad de Costa Rica

Facultad de Ingeniería

Escuela de Ciencias de la Computación E Informática

 

 

 

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 1”.

 


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 la Computación e Informática.

/// \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

Hosted by www.Geocities.ws

1