eclipse-800x426.png

Como usar Eclipse en todo su esplendor

No más tantos dolores de cabeza

--Joaquín Azcárate y Francisco Bravo


Índice

Primero y principal

Snippets/Templates

Explicacion

Crear Snippets propios

¡Extra! ¿Extra? ¿Dijo extra?

Debug Interactivo

Empezando

Qué pretende esta sección

Primer Programa a Debuggear

Entendiendo la perspectiva de Debug

Cómo correr un programa

Vista Variables

Programa Complejo

Breakpoints

Casteos en Variables

Programa Complejo Complejo

Editar memoria on the fly

El error que no es

Breakpoints RELOADED

Refactors

Definición

Cómo hacerlo

Rename

Extract Constant:

Extract Local Variable

Extract Function

Conclusiones


Primero y principal

La razón por la que confeccionamos este compilado de tres guías, todas sobre Eclipse, es porque creemos que la mayor parte del tiempo que van a pasar desarrollando el TP de Sistemas Operativos, lo van a pasar frente a Eclipse; por lo que intentamos hacerles aprender esta herramienta de una forma lo más feliz posible.

Si detectan algún error, o sienten que algo se explicó mal, poco o no está, pueden contactarme a: os@florius.com.ar, u cualquier otro ayudante.

Ahora sí, una vez sacados de encima esa introducción molesta; abrimos el Eclipse.

Snippets/Templates

Explicación

Eclipse nos otorga la posibilidad de generar nuestros propios atajos (snippets/templates), cuyo fin es acelerar el proceso de programación. Los Snippets funcionan como esquemas de una porción de código; la cual, cada vez que utilicemos el atajo, se auto “escribirá” sola. Por ejemplo, tener un atajo para sentencias específicas como pueden ser los if’s, for’s, while’s, entre otros.

Crear Snippets propios

  1. Para poder generar nuestros Snippets deberemos ir a la solapa “Window” (del Eclipse) y escoger la opción “Preferences”.
  2. Se abrirá una ventana, en la que deberemos abrir la opción “C/C++”, lo que mostrará más opciones. De estas opciones, nos interesa ir dentro de “Editors”. Dentro de Editors, lo que buscamos es “Templates”.

    El cuadro en verde nos muestra la lista de templates, como podemos ver, el Eclipse trae varios ya definidos.
  3. Lo que nos interesa es crear nuestro propio template. Para eso, clickeamos la opción “New…”, lo que nos muestra una ventana en la cual podemos definir nuestro template. El campo “Name”, va a ser el nombre de nuestro atajo (Dahh, obvio), pero además, este nombre será lo que debamos usar para utilizar el atajo. El campo “Description”, sirve para dar una descripción de la funcionalidad del atajo. El “Context”, indica el lenguaje en el que se aplicará el atajo. El “Pattern”, es el código específico que queremos que se autogenere; además, Eclipse nos proporciona ciertas variables para ayudarnos.
    Ejemplos de variables: ${cursor} posiciona el cursor en el lugar indicado, ${date} escribe la fecha; revisar en “Insert Variable…” para ver otras variables
  4. Ya está, le damos a OK y nuestro atajo está creado, lo único que falta es ponerse a usarlo. Para esto, solo tenemos que abrir uno de nuestros archivos de código, escribir el nombre del atajo y usar [ctrl+espacio].No es necesario escribir el nombre entero del atajo, ya que Eclipse busca atajos que empiecen con lo que hemos escrito. En el ejemplo anterior, ya con escribir “da” es suficiente.

¡Extra! ¿Extra? ¿Dijo extra?

Otro elemento que nos proporciona Eclipse son las Task Tags, no son la gran cosa, pero ayudan. Nos sirven para colocar un indicador en nuestro código, ya sea como recordatorio o como forma de acceder rápido a una sección del código sin tener que andar revisándolo todo.

Meh, no es tan interesante, pero quiero ver cómo se hacen

Al igual que con los templates, debemos ir a Window, Preferences, C/C++; pero ahora queremos ir a Task Tags. Donde veremos una lista con todos los tags, el Eclipse ya trae algunos por default.

Para agregar nuestros propios tags, solo debemos ingresar en “New…” y crearlo. Luego solo queda usarlos. Los Task Tags deben ser utilizados en comentarios, de otra forma, Eclipse no los reconocerá como tales.

En la imagen podemos ver que los Tags aparecen en letra negrita y con color azul, además en la barra scrolleable del código nos aparecen indicadores de la posición de dichos Tags.

Debug Interactivo

Empezando

Qué pretende esta sección

La idea es que terminen de leer esta guia y sean capaces de entender y afrontar cualquier error de programación que tengan. No se cubrirán errores de sintaxis o de semántica, simplemente es una descripción del entorno de Eclipse CDT y su interfaz gráfica del debugger de C (GDB[1]).

Todo lo que se ve en este documento es simplemente una forma linda de correr GDB, por lo que los conocimientos son independientes del Eclipse CDT. Para comprender los comandos de GDB que Eclipse nos brinda de forma visual, los redirijo a la documentación de GDB.

Todas las imagenes estan sacadas usando Eclipse IDE for C/C++ Developers, versión: Juno Service Release 2; build id: 20130225-0426[2].

Posiblemente su Eclipse se vea un poco diferente, como saque las imagenes en una resolución pequeña, voy a estar moviendo y escalando los diferentes marcos.

Esta guía plantea un flujo de trabajo que pretende ser interactivo, por lo que les recomiendo seguir, localmente, a la par estos conceptos, detenerse, pensar, probar, codificar, o lo que sientas en tu corazón al ver esto:

Vale hacer uso del índice para consulta.

Posiblemente, si quieren debugear ya tengamos un proyecto creado, pero a modo de ejemplo, creen un nuevo proyecto haciendo File > New > C Project > [Ponen un nombre al proyecto] > Linux GCC > Finish. Ahora con un proyecto vacío, podemos hacerle Click derecho a este > New > Source file > [Le dan un nombre al nuevo archivo]; en mi caso, mi proyecto se llama “Ejemplo” con un único archivo “principal.c

Supongamos que quieren un programa que cree una variable entera, le asigne a esta nueva variable el número 4, y devuelve este valor. Los aliento a que piensen y programen el problema planteado antes de avanzar, así pueden seguir la guía en sus propias computadoras. Suele ser mejor aprender haciendo que solo leyendo; pero ¿que voy a saber yo? Soy un documento en internet.


Primer Programa a Debuggear

Una vez que tengan programado algo, pueden apretar el boton de Run Debug, esto ejecutará su programa en modo de debug, donde pueden seguir paso a paso el flujo y los datos de su programa.

Si es la primera vez que apretan el boton de Run Debug les preguntara si quieren cambiar de perspectiva; una perspectiva es una forma diferente de ver el Eclipse donde cambian las vistas. Lo más cómodo es pedirle que recuerda que “Si, quiero cambiar la perspectiva”. De apretar no, quedaran en la perspectiva de programacion, para ir a la perspectiva de Debug pueden ir a Window > Open Perspective > Other... > Debug.

De ahora en adelante les aparecera la perspectiva de Debug a la izquierda, junto con la perspectiva de programación, y pueden ir intercambiandolas a medida que necesiten programar / debuggear.

Entendiendo la perspectiva de Debug

En esta perspectiva tienen 5 marcos diferenciables:

  1. Vista Debug donde les muestra que esta ejecutando en este momento y porque no avanza su programa. En esta imagen se ve que esta ejecutando el proyecto “Ejemplo [C/C++ Application]”, el ejecutable “Ejemplo” con PID 20361 en el núcleo 0, con un hilo TID 20361, suspendido por un breakpoint, con el stack con una sola función “main()”.
  2. Un marco con las vistas de Variables, Breakpoints, y otras dos:
  1. Vista Variables muestra todas las variables locales en el stack actual.
  2. Breakpoints muestran los breakpoints que tenemos asignados.
  1. Vista de archivos, particularmente solo vemos nuestro único archivo “principal.c” y esta pintada la línea a ejecutar (todavia no se ejecuto a=4)
  2. Vista de Outline, idéntica a la de programación, es una forma de acceder rápidamente a las funciones.
  3. Marco con Console, que muestra lo que mostraría la consola de ejecutarlo normalmente, sin Eclipse, y otros agregados

Cómo correr un programa

  1. Como ya vimos, el boton Run Debug empieza la ejecución en modo Debug; antes de apretar este, el resto de los botones marcados estarían en gris.
  2. Ignorar todos los breakpoints, no frena en ningun breakpoint.
  1. Resume (F8), Ejecutar todo lo que puedas hasta que encuentra un breakpoint activo (que no esté ignorado)
  2. Stop, Terminar el proceso abruptamente
  3. Step Into (F5), Ejecutar en la mínima granularidad. De haber un llamado a funcion va a entrar a ella.
  4. Step Over (F6), Ejecutar toda la linea, de ser una línea con llamadas a funciones, las ejecuta pero no frena en ninguna (a menos que exista un breakpoint activo dentro de alguna función llamada)
  1. Step Return (F7), Ejecuta el resto de la función actual hasta llegar a la línea de donde fue llamada. En este ejemplo al tener solo la función de Return, no podemos “salir”.


Vista Variables

Recordemos a donde quedamos, estamos con nuestro programa corriendo en modo Debug, por lo que fren antes de ejecutar cualquier cosa. Tenemos marcada la primera línea, por lo que todavía no se ejecutó. Si ponemos el mouse arriba de la letra ‘a’ veremos que es de tipo int con el valor 0. Esto mismo podemos verlo en la vista de Variables. Junto con las variables locales, nos aparecen los parámetros de la función; en este caso argc y argv.

Intentemos recordar cómo podemos ejecutar esta linea. Pausa dramática y ruido de “pensamiento”.

Si adivinaron Step Into o Step Over estan en lo correcto

Como vemos, ahora esta pintada la línea siguiente; el valor de a se alteró y se pintó de amarillo

Programa Complejo

Supongamos que nos encarga la NASA de armar un programa que sea capaz de almacenar patentes (3 letras y 3 números); y queremos almacenar las patentes: “ABC 123”, “SQL 035”, “UTN 999”.

Devuelta, los invito a pensar la solucion en C.

Posiblemente tengan una solución como esta[3]

Intentemos correrlo como debug y frenar justo antes de insertar la palabra “UTN”.

Entonces correriamos en debug este programa y apretaríamos 6 veces Step Over. Tiene que existir una mejor forma de hacer esto, y no tener que contar los Steps...

Breakpoints

Tan simple como hacer doble click en la barra a la izquierda de la línea de código que queremos interrumpir. Esto se puede hacer tanto en la perspectiva de programación C/C++, como en la de Debug. Incluso se pueden hacer mientras el programa esté ejecutando (o frenado) en modo Debug.

Adicionalmente en la perspectiva de Debug, esta la vista de “Breakpoints” donde pueden ver los breakpoints que tengan, y desactivarlos temporalmente haciendole click al tick.

Ahora podemos correr en Debug nuestro programa y darle Resume, la ejecución se va a trabar donde pusimos el breakpoint, sin ejecutar la línea que fue trabada.

Cuando logres hacer esto, intenta mirar la variable local “patentes”.

Como esta variable es un puntero, GDB no puede suponer si es un puntero, o si es un vector, por lo que cuando lo ves con la vista solo van a ver el primero ¿Como podemos arreglar esto?

Casteos en Variables

Si uno apreta Click derecho en la variable que queremos, nos presenta la opción de ver la memoria tal cual la grabo o  castear a un tipo particular o, como queremos ahora, mostrar cómo una array.
Recordemos que esto ya esta en ejecución, por lo que si quisiéramos mostrar como un array de 100 elementos, la patente 4 en adelante estarían fuera del segmento alocado y tendría que dar un fallo de segmento. Pueden probarlo, pero esto no pasa porque solo nos muestra la memoria. Si casualmente hay algo nos va a mostrar algo corrupto, o si no esta asignado nada después de la posicion asignada, nos dice que no puede accederla

(Si uno hace doble click en una pestaña, particularmente la vista de Variables se agranda. Volver a hacer doble click vuelve todo a la normalidad)

Y vemos exactamente el valor de cada variable dentro de un vector. Podríamos tener un vector dentro de otro, por lo que repetimos este proceso de Click derecho > Display as Array...


Programa Complejo Complejo

Supongamos que queremos refactorizar nuestra solución y generar un TAD de patentes; algo que se vea asi:

Y queremos un resultado en la consola como:

Patente 0: ABC 123

Patente 1: SQL 035

Patente 2: UTN 999

Antes de empezar a pensar la solución, esto es lo que pasaría de correr como Debug ese código:

¿Notas como en la vista de Variables no esta la variable “patentes”?

Esto no es un “bug”, sino que como patentes fue definida de forma global, no aparece junto con las variables locales ¿Entonces como podemos ver el contenido?

Una solución válida es poner el mouse arriba, pero aprovecho esta situación para introducirles la vista de Expresiones.

Muy posiblemente no tengan esta vista por defecto; para accederla uno puede ir a Window > Show View > Expressions.

Ahora tenemos en el mismo marco que Variables, una nueva pestaña de expresiones con un +, donde podemos hacer click y escribir la expresión que queramos. Recordemos que el programa ya está ejecutando, por lo que no podemos pedir que ejecute una funcion, solo podemos ver variables.

Si escribimos “patentes” podemos acceder a la variable global, tal como haríamos en la vista de Variables.



Ahora si, a programar una posible solución

Supongamos que generamos esta función que inicializa nuestro TAD, e intentamos ejecutar esta instrucción:

agregarPatenteAVector(crearPatente("ABC", 123), patentes);

Tenemos una funcion cuyo argumento es otra funcion.


Supongamos un código de crearPatente de este estilo:

t_patente* crearPatente(char* letras, unsigned short numeros){

        t_patente* ret = malloc( sizeof(t_patente) );
        ret->letras =
strdup(letras);
        ret->numero = numeros;
        
return ret;

}

Al hacer Step Into podemos ver que en el stack (dentro de la vista de Debug) aparece que estamos dentro de la función crearPatente(), llamada por main().

Editar memoria on the fly

Antes de seguir adelante, es una buena oportunidad de comentarles que dentro de la vista de Variables (y de Expresiones) uno puede alterar el valor de una variable. Haciendole doble click a el Value, uno puede ingresar lo que quiera y de ese punto en adelante, el programa tendrá ese nuevo número. Usar con cuidado.

Ahora estamos en una línea con una llamada a una funcion que no programamos nosotros: malloc[4]. Si apretamos Step Into, ¿qué tendría que pasar?

El error que no es

Si, se merece todo un título para esto nomas.

Al igual que el Step Into anterior, el stack crece y se llama a malloc, pero como esta programado por otras personas, y no tenemos acceso al código, Eclipse nos informa que no puede encontrar el código. Si puede encontrar la biblioteca compartida y ejecutar el código; el problema es que ese código tiene a su vez varias llamadas a otras funciones y varias líneas, por lo que entrar en pánico y apretar Step Into y Step Over nos lleva más adentro de la madriguera del conejo.

Lo que nos lleva a la salvación con el Step Out, que nos devuelve a la función de donde se llamó, en este caso crearPatente().


Breakpoints RELOADED

Una vez que tenemos breakpoints, uno puede hacer click derecho en la pelotita e ir a sus propiedades.

Por ejemplo podríamos querer un breakpoint que solo interrumpe la ejecución en el codigo de imprimir las patentes, si las letras del vector a mirar son “UTN”

Notar que en esta condición tenemos una llamada a una funcion y evaluamos el retorno. Esto se puede hacer.

Una vez creado este nuevo breakpoint, podríamos resumir nuestra solucion y ver si efectivamente frena en algún lugar.

Refactors

Abajo lo viejo y arriba el refactor

Los ejemplos de código que se mostraran en esta sección de la guía solo tienen como objetivo demostrar el funcionamiento de los refactors y no están pensados como ejemplos de buen código.

Definición

Un refactor se lo llama a realizar un cambio en el código fuente, pero que a la vez, su funcionamiento es el mismo. Es decir, cambiar la estructura interna de nuestro código y mantener el comportamiento externo.

El motivo por el que queremos hacer refactors es incrementar distintas cualidades de nuestro código; como puede ser, la expresividad, la consistencia, la flexibilidad, entre otros.

Cómo hacerlo

Si vamos al Eclipse, podemos notar que una de la opciones en la barra superior tiene el nombre “Refactor” (¿Quizás nos puede servir, no?). Al hacer click en esta opción, se despliega un listado de posibles refactors bastante comunes, para los que el Eclipse ya viene automatizado.

Analicemos cada refactor:

Rename

Veamos el siguiente código:

Como se puede deducir, no es sencillo saber qué es lo que hace este código, principalmente por el nombre de las variables. En este caso, podemos utilizar el Rename, para renombrar cada una de estas variables de una forma automatizada, en lugar de andar renombrando cada aparición de las mismas.

Como se ve en la imagen, el rename recuadra todas las apariciones de la variable y nos pide ingresar el nuevo nombre. Una vez escrito el nombre, hay que apretar Enter. En la imagen ya podemos ver renombradas las variables bar a maximo y foobar a vector_numeros.

Con este refactor logramos que nuestro código sea más expresivo y fácil de entender.

También, si miramos la función not_main() vemos que el IDE reconoce el scope de las variables, ya que solo toma aquellas variables foo del contexto en el que aplicamos el refactor.

Extract Constant:

Cambiemos nuestro código a lo siguiente:

Se puede notar que tanto la cadena “password” como el número entero 4 son constantes del programa, las cuales, quizás sería bueno abstraer y definir como constantes. ¿Para qué? Imaginemos que la cadena “password” se emplea en 40 líneas de nuestro código y que por algún motivo, es necesario cambiarla por “contraseña”; habría que ir a las 40 líneas y cambiar la cadena. En estos casos, es beneficioso definir constantes. El refactor Extract Constant se encarga precisamente de esto, basta con seleccionar nuestra constante (en este caso “password” y el número 4), ir a la solapa “Refactors” y elegir “Extract Constant”. Nos aparecerá un cartel pidiéndonos el nombre de la constante a definir y una vez hecho ya tenemos el código refactorizado.

Al hacer este refactor pudimos deshacernos de la variable palabra_clave que se utilizaba en el if y en el segundo printf. Más allá de esto, logramos que nuestro código sea más flexible a cambios.

Si bien este extraer en constantes tiene sus beneficios, es muy importante saber identificar cuándo se está tratando con una constante y cuándo no.

Extract Local Variable

Ahora se nos plantea la siguiente situación:

Podemos notar que el cálculo subrayado en rojo es nuestro inconveniente. Supongamos que dicho cálculo es incluso más complejo aún. ¿Qué tan expresivo resultaría el cálculo?¿Podrían saber qué es lo que resuelve solo viéndolo? Lo más probable es que no. Acá es cuando entra el refactor de Extract Local Variable. Con este refactor podemos asociar este cálculo a una variable en la que podemos definir un nombre expresivo de lo que realiza.

De esta forma es más expresivo nuestro código; y volviendo a refactors anteriores, supongamos que el cálculo pasa a ser una operación completamente distinta, en esta nueva situación podríamos modificar lo que se asigna a nuestra variable y luego renombrar la variable, lo que nos facilitaría mucho trabajo (considerando que dicho cálculo modificado se emplea en muchas secciones del código).

Extract Function

Continuando con el código luego de Extract Local Variable:

Podemos ver que ambos recuadros son claramente distintas funcionalidades de nuestro programa. Una buena práctica es modularizar nuestro código, de tal manera que cada función realice una funcionalidad específica (como en este caso, una función calcula el valor de la operación y la otra función valida si el resultado es par o impar y muestra por pantalla). Al seleccionar la opción Extract Function nos abre la siguiente ventana:

Nos pide un nombre de función (Function name), una de las 3 opciones para Access modifier (En C cualquiera de las 3 es lo mismo, ya que no tiene definidos ninguno de ellos), y ya nos infiere la lista de parámetros que va a requerir nuestra función (con la opción Edit… podemos cambiar el nombre de los parámetros y con las opciones Up/Down podemos cambiar el orden en que recibe los parámetros).

En nuestro caso particular, yo extraje los 2 recuadros rojos en 2 funciones, funcion_calculo() y es_par(); quedando así:

De esta forma, subdividimos las funcionalidades. Esto nos permite abstraer cómo una función realiza una operación y simplemente mostrar qué es lo que hace.

Conclusiones

Si bien ya vimos los refactors que nos ofrece Eclipse para la programación en C, no hay que olvidar que se debe analizar cada situación y poder ver cuándo resulta conveniente aplicar un refactor y cuál de todos (o qué combinación de refactors) es la mejor opción. Como es una herramienta que se puede hacer siempre, hay que saber cuándo aplicarla.

The End

Ahora, a debugear!


[1] http://www.gnu.org/software/gdb/

[2] Es la que está en la máquina virtual entregada por la cátedra en el 2013 y 2014

[3] El código este pierde memoria. Documentación sobre el correcto uso de la memoria.

[4] sizeof es un operador, no una función.