08 agosto 2008

Pasando de Delphi 7 a RAD Studio 2007 (5)

Hoy vamos a comenzar a ver las novedades que incorpora RAD Studio 2007 en el lenguaje de programación Object Pascal respecto a Delphi 7.

REGISTROS CON MÉTODOS

Si hasta ahora no se le daba mucha importancia a los registros (record) a favor de las clases para crear y almacenar nuestros propios tipos de datos, ahora podemos aprovecharlos de nuevo con la posibilidad de añadir métodos como si fueran clases.

Y no sólo eso, sino que además se pueden tener su propio constructor e incluso métodos y propiedades. De este modo podemos encapsular tanto los datos así la manipulación de los mismos dentro de un mismo registro y además gozando de la ventaja de no tener que instanciar ni liberar la clase.

Veámoslo con un ejemplo. Supongamos que tenemos este registro para definir un artículo:

type
TArticulo = record
ID: Integer;
Nombre: String;
Existencias, Precio: Real;
end;

Pues ahora podemos hacer algo como esto:

TArticulo = record
var
ID: Integer;
Nombre: String;
Existencias, Precio: Real;

constructor Create( ExistenciasIniciales, Precio: Real );
end;

La palabra reservada var no es obligatoria, aunque deja el código más elegante. Aquí tenemos la implementación de su constructor:

constructor TArticulo.Create(ExistenciasIniciales, Precio: Real);
begin
Existencias := ExistenciasIniciales;
Self.Precio := Precio;
end;

De este modo podemos crear un artículo al igual que una clase sin instanciarlo:

var
Articulo: TArticulo;
begin
Articulo.Create( 10, 132.25 );
ShowMessage( 'Existencias=' + FloatToStr( Articulo.Existencias ) + #13 +
'Precio=' + FloatToStr( Articulo.Precio ) );
end;

Nos mostraría esto en pantalla:


También podemos crear propiedades que accedan a las variables encapsulando de este modo todo el registro:

type
TArticulo = record
var
ID: Integer;
Nombre: String;
Existencias, Precio: Real;
Comprado, Vendido: Real;

constructor Create( ExistenciasIniciales, Precio: Real );
procedure Comprar( Unidades: Real );
procedure Vender(Unidades: Real);
function CalcularExistencias: Real;
property ExistenciasReales: Real Read CalcularExistencias;
end;

Esta sería su implementación:

constructor TArticulo.Create(ExistenciasIniciales, Precio: Real);
begin
Existencias := ExistenciasIniciales;
Self.Precio := Precio;
Comprado := 0;
Vendido := 0;
end;

procedure TArticulo.Comprar(Unidades: Real);
begin
Comprado := Comprado + Unidades;
end;

procedure TArticulo.Vender(Unidades: Real);
begin
Vendido := Vendido + Unidades;
end;

function TArticulo.CalcularExistencias: Real;
begin
Result := Existencias + Comprado - Vendido;
end;

Ahora aprovechamos del registro Articulo como si fuera una clase:

var
Articulo: TArticulo;
begin
Articulo.Create( 10, 132.25 );
Articulo.Comprar( 100 );
Articulo.Vender( 50 );
ShowMessage( 'Existencias Reales=' + FloatToStr( Articulo.ExistenciasReales ) + #13 +
'Precio=' + FloatToStr( Articulo.Precio ) );
end;

Este sería el resultado:


Sin embargo, los registros todavía no gozan de muchas de las características que poseen las clases:

- Un registro no puede tener destructores.

- No se pueden crear métodos virtuales.

- No se puede heredar de otro registro.

LA SOBRECARGA DE OPERADORES EN REGISTROS

La sobrecarga de operadores nos permite operar con los registros como si fueran valores numéricos. Por ejemplo:

TFactura = record
private
Importe: Real;
public
class operator Implicit( Importe: Real ): TFactura;
class operator Add( F1, F2: TFactura ): TFactura;
class operator Subtract( F1, F2: TFactura ): TFactura;
end;

Las palabras reservadas class operator sirven para redefinir los operadores +, -, =, etc. que se utilizan al tratar con los registros. En esta ocasión sólo he redefinido los operadores de suma y resta para aplicarlos a la factura.

Esta sería su implementación:

class operator TFactura.Implicit( Importe: Real ): TFactura;
begin
Result.Importe := Importe;
end;

class operator TFactura.Add( F1, F2: TFactura ): TFactura;
begin
Result.Importe := F1.Importe + F2.Importe;
end;

class operator TFactura.Subtract( F1, F2: TFactura ): TFactura;
begin
Result.Importe := F1.Importe - F2.Importe;
end;

El operador Implicit se utiliza para que podamos asignar valores al registro directamente como si fuera un valor numérico:

var
F1, F2, F3: TFactura;
begin
F1 := 50;
F2 := 100;
F3 := 20;
F1 := F1 + F2 - F3;

ShowMessage( 'Total facturas=' + FloatToStr( F1.Importe ) );
end;

Como puede apreciarse en el código he asignado los valores directamente al objeto en vez de hacer esto:

F1.Importe := 50;
F2.Importe := 100;
F3.Importe := 20;

Esto me lo ha permitido el operador Implicit. Luego he sumado las dos primeras facturas y les he restado la tercera:

F1 := F1 + F2 - F3;

Que equivale a esto:

F1.Importe := F1.Importe + F2.Importe – F3.Importe;

Al ejecutar este código este es el resultado:


Esto nos da mucha potencia a la hora de crear registros para operar con facturas, recibos, calcular saldos, asientos contables, etc.

LOS BUCLES FOR IN

Esto es algo que ya se echaba de menos respecto a otros lenguajes más modernos que Pascal, como pueden ser Java, Python, Ruby, C#, etc. Es la posibilidad de poder crear un bucle sin tener que crear una variable entera para recorrerlo.

Por ejemplo, si tengo que recorrer un array y sumar su resultado esto es lo que tenía que hacer hasta ahora:

var
Facturas: array[1..3] of Real;
Suma: Real;
i: Integer;
begin
Facturas[1] := 10;
Facturas[2] := 20;
Facturas[3] := 30;

for i := 1 to 3 do
Suma := Suma + Facturas[i];

ShowMessage( 'Suma=' + FloatToStr( Suma ) );
end;

Con este método tenemos que saber el número de elementos en el array. Ahora, mediante el bucle for in podemos recorrer un array sin tener que saber cuantos elementos contiene:

var
Facturas: array[1..3] of Real;
Suma, Factura: Real;
begin
Facturas[1] := 10;
Facturas[2] := 20;
Facturas[3] := 30;

for Factura in Facturas do
Suma := Suma + Factura;

ShowMessage( 'Suma=' + FloatToStr( Suma ) );
end;

En este caso ya no es necesaria la variable entera i para recorrer los elementos del bucle. Sólo hace falta una variable que contenga el elemento actual (Factura). Esto es muy útil para recorrer arrays dinámicos sin preocuparse en el número de elementos que tienen.

Aunque esto lo considero algo incompleto, porque no se puede hacer algo como esto:

var
Componente: TComponent;
begin
for Componente in Form1.Components do
ShowMessage( Componente.Name );
end;

Al compilarlo nos da este error:

‘[‘ Expected but ‘DO’found

Es decir, que esperaba los corchetes después de la palabra Components. Si no podemos utilizar un for in para este tipo de elementos la verdad es que nos quedamos casi igual que antes. Si sólo podemos utilizarlo en arrays y en colecciones simples la verdad es que se nos queda algo corto.

LA DIRECTIVA INLINE

Esta directiva puede aplicarse a funciones o procedimientos modificando el modo de compilación de los mismos. Vamos a verlo con un pequeño ejemplo.

Este es el típico bucle para ordenar los elementos de un array por el método de la burbuja:

var
i, j, tmp: Integer;
Elementos: array[1..10000] of Integer;
Tiempo: Dword;
begin
// Rellenamos el array con valores aleatorios

Randomize;
for i := 1 to 100 do
Elementos[i] := Random( 500 ) + 1;

Tiempo := TimeGetTime;

// Ordenamos los elementos del array con el método de la burbuja

for j := 1 to 9999 do
for i := 1 to 9999 do
if Elementos[i] > Elementos[i+1] then
begin
tmp := Elementos[i];
Elementos[i] := Elementos[i+1];
Elementos[i+1] := tmp;
end;

ShowMessage( 'Milisegundos=' + IntToStr( TimeGetTime - Tiempo ) );
end;

Para poder utilizar la función TimeGetTime es necesario añadir en uses la unidad mmsystem. Lo que he hecho es crear un array de 10000 números enteros y les he metido a cada uno un valor aleatorio entre 1 y 500. Después he utilizado el método de la burbuja para ordenarlos. Al ejecutar el programa me tarda 155 milisegundos:


Ahora supongamos que quiero refactorizar el código y quiero sacar a parte el núcleo del bucle en otro procedimiento;

procedure Intercambiar(var a, b: Integer);
var
tmp: Integer;
begin
tmp := a;
a := b;
b := tmp;
end;

De modo que el procedimiento original se nos quedaría así:

var
i, j: Integer;
Elementos: array[1..10000] of Integer;
Tiempo: Dword;
begin
// Rellenamos el array con valores aleatorios

Randomize;
for i := 1 to 100 do
Elementos[i] := Random( 500 );

Tiempo := TimeGetTime;

// Ordenamos los elementos del array con el método de la burbuja
for j := 1 to 9999 do
for i := 1 to 9999 do
if Elementos[i] > Elementos[i+1] then
Intercambiar( Elementos[i], Elementos[i+1] );

ShowMessage( 'Milisegundos=' + IntToStr( TimeGetTime - Tiempo ) );
end;

Al ejecutar el programa vemos que tarda más:


Esto tiene su explicación: cada llamada al procedimiento Intercambiar obliga a realizar un salto a otro procedimiento para luego volver a este. Si el bucle no tiene muchas iteraciones el cambio no suele notarse mucho, pero si se realiza miles de veces entonces la diferencia de tiempo entre uno y otro método suele ser notable.

Para solucionar esto tenemos la directiva inline. Esta directiva aplicada al procedimiento Intercambiar hace que se compile el código de este procedimiento como si realmente nunca lo hubiésemos sacado del bucle. De este modo ganamos claridad en el código fuente sin perder rendimiento en el código compilado.

Sólo hay que añadir la palabra inline al final del procedimiento:

procedure Intercambiar(var a, b: Integer); inline;
var
tmp: Integer;
begin
tmp := a;
a := b;
b := tmp;
end;

Sin ser tan rápido como la manera original si se obtiene un aumento de velocidad:



Quizás en un programa de gestión no es necesario utilizar esta función, pero en la programación de videojuegos algunas funciones se ejecutan tantas veces por segundo que puede marcar la diferencia entre renderizar un mundo 3D con suavidad a 40 o 50 frames por segundo o relentizarse y dar tirores a 20 frames por segundo. Cuando escriba en un futuro articulos de programación de videojuegos 3D con SDL y OpenGL veréis a que me refiero.

En el próximo artículo seguiremos viendo otras novedades en el lenguaje.

Pruebas realizadas en RAD Studio 2007.

01 agosto 2008

Pasando de Delphi 7 a RAD Studio 2007 (4)

Hoy vamos a ver dos novedades muy importantes en las que aventaja RAD Studio 2007 respecto a Delphi 7: las Auditorias y las Métricas.

LAS AUDITORIAS

Las auditorias nos permiten rastrear todo el código fuente en busca de errores de diseño, implementación y ejecución. Si bien no todo lo que muestran son errores, si que da importantes consejos sobre como implemenar el código correctamente y evitar posibles errores.

Para realizar una auditoria hay que seguir estos pasos:

1. Seleccionamos la pestaña Model View en la sección del administrador de proyectos (Project Manager):


2. Pulsamos el nombre del proyecto con el botón derecho del ratón y seleccionamos QA Audits:


Se abrirá esta ventana:


Por defecto aparecen casi todas las comprobaciones activadas:

Arrays and References (colecciones y referencias): comprueba que los arrays y las referencias a objetos no apunten a valores nulos, que no excedamos el límite de un array, campos no inicializados, etc.

Branches and Loops (bifurcaciones y bucles): examina las sentencias de control para evitar bucles infinitos, si hay bloques de código que nunca serán ejecutados, etc.

Coding Style (estilo de código): esta sección comprueba inconsistencias en el código tales como comprobar si hay variables globales que son modificadas por varios bloques de código distinto con posibilidad colisión o si por ejemplo hay varias sentencias de código de una sola línea que podrían refactorizarse.

Declaration Style (estilo de declaración): comprueba si hay métodos o variables públicas que podrían estar como privados, variables que podían ser estáticas o la sobrecarga de métodos no abstractos.

Design Flaws (Imperfecciones en el diseño): comprueba si hay subclases que tienen miembros con el mismo nombre o variables que se han declarado como temporales en toda la clase pero que son utilizadas (peligrosamente) por varios métodos.

Duplicated Code (Código duplicado): revisa el código fuente en busca de bloques de código duplicados, ya sean bloques de código que se encuentran en distintas sentencias de control como en la creación de constructores de clase.

Expressions (Expresiones): revisa el código en busca de posibles divisiones por cero, el paso de argumentos a funciones con posibles valores nulos, desbordamientos positivos o negativos en variables enteras, etc.

Naming Style (Estilo de nombres): comprueba si los nombres de las variables, objetos o clases que hemos declarado tienen correctamente puestas las mayúsculas y minúsculas o si por ejemplo el nombre de una interfaz tiene el prefijo I.

Performance (comportamiento): comprueba si as ha sido utilizado como un operador o si se ha duplicado la conversión (typecast) de una variable dos o más veces.

Possible Errors (errores posibles): inspecciona el código en busca de dos bifurcaciones o más que provoquen el mismo resultado (código repetido). También mira el retorno de valores que no han sido chequeados, posibles conversiones de tipos de variable erroneos, etc.

Superflous Content (contenido superfluo): comprueba si hay miembros de clase que nunca serán utilizados, conversiones entre variables no son necesarias o si se han pasado parámetros a funciones o procedimientos que nunca serán utilizados.

Sólo tenemos que pulsar el botón Start para que comience la auditoria:


Al terminar nos mostrará en la parte inferior del editor de código el resultado de la auditoria:


En la columna Abbreviation nos indica el posible tipo de error que hemos cometido. Estos son algunos de sus significados:

NC -> Naming Concencions (conveniones de nombres).
USP -> Use Singleton Pattern (usar el patrón singleton).
FNI -> Field Not Initialized (campo no inicializado).
MCS -> Member Can be made Static (el miembro puede ser estático).

El modo de proceder a su corrección es algo extraño. Lo normal sería que si hago doble clic sobre una supuesta línea de error debería saltar a esa línea, pero no hace nada. Tenemos que pinchar la línea de error con el botón derecho del ratón y seleccionar Show Description:


Entonces abrirá una nueva pestaña encima del editor de código mostrando la descripción del error:


Ahora si hacemos doble clic sobre la línea del error entonces se cambiará de nuevo a la pestaña del editor y seleccionará el código fuente donde se encuentra el problema:


El único inconveniente que le veo a esto es que no sitúa el editor en la línea donde está el supuesto error, sino que tenemos que ser nosotros los que movamos la barra de desplazamiento lateral del editor de código hasta encontrar el código fuente seleccionado (según el número de línea). Lo tenían que haber hecho un poco más cómodo, al igual que cuando compilamos.

A veces no todos tienen porque ser errores. En la columna Severity tenemos generalmente dos tipos de información: Warning (advertencia) e Info (Información). Por ejemplo, si yo he creado una variable con notación húngara llamada sNombre me mostrará una advertencia diciendo que hemos incumplido la forma de llamar a las variables según la convención del lenguaje Pascal, donde se supone que el nombre que se le da a una variable, clase, etc. tiene que empezar por mayúsculas.

También puede quejarse si no he colocado las palabras begin y end en bloques de código de una sóla línea. Por ejemplo:

if Importe < 100 then
ShowMessage( 'Existencias mínimas' );

Según la auditoria debería hacerlo de este modo:

if Importe < 100 then
begin
ShowMessage( 'Existencias mínimas' );
end;

Así que no hay que hacerle demasiado caso a estas advertencias ya que cada programador tiene su propio estilo de escritura de código. En la parte izquierda de la ventana de auditorias tenemos los botones para guardar el resultado de la auditoria (en XML o HTML), imprimirla, refrescarla o volver a realizarla:
Hay que tener mucho cuidado con proyectos que son muy grandes ya que realizar una auditoria de los mismos puede llegar a ser desesperante. De hecho, RAD Studio se queda bloqueado de tal modo que deja Windows medio tonto.

Si tienes un proyecto con miles de líneas de código lo mejor es que selecciones en el Model View sólo el formulario que quieres auditar.

LAS MÉTRICAS

Las métricas son otro modo de evaluar nuestro código fuente estableciendo una serie de reglas que nos permitan por ejemplo fijar un máximo número de líneas por bloque de código o el número máximo de bucles anidados. Esto puede ser muy útil para fijar unos máximos y mínimos a la hora de implementar el código fuente cuando un proyecto lo realizan dos o más programadores y conviene que todos sigan un mismo estilo.

Los pasos para analizar la métrica de nuestro código son muy parecidos a los de las auditorías:

1. Seleccionamos la pestaña Model View en la sección del administrador de proyectos (Project Maganer).

2. Pulsamos el nombre del proyecto con el botón derecho del ratón y seleccionamos QA Metrics:


Se abrirá esta ventana:


Aparecen activadas todas estas opciones:

Basic (básico): cuenta el número de líneas de código de cada unidad, interfaz, clase, etc.

Cohesion (Cohesión): comprueba la cohesión de cada clase contando el número de miembros y métodos de cada clase.

Complexity (complejidad): comprueba el número máximo de bifurcaciones en el código, el número de variables locales y el peso de cada clase (a mas líneas de implementación, más pesada).

Coupling (acoplamiento): calcula entre otras cosas los valores medios y ratios máximos en el uso de interfaces, en la duplicación de métodos abstractos y el número de accesos a variables de clase directamente o mediante métodos.

Encapsulation (encapsulación): comprueba el factor de ocultación de los miembros y métodos de cada clase.

Halstead: calcula la proporción entre el número de operadores y el número de operandos.

Inheritance (herencia): comprueba el factor de herencia entre clases, la profundidad entre la jerarquía de clases heredadas así como el número de clases hijas.

Inheritance-based coupling (acoplamiento basado en la herencia): calcula el ratio y la media de herencia entre clases.

Maximum (máximo): comprueba el número máximo de niveles de identación entre los bloques de código anidados.

Polymorphism (polimorfismo): cuenta el número de métodos añadidos o sobrecargados y el ratio de polimorfismo.

Ratio: cuenta el número de líneas de comentario que hay en el código y en la documentación.

Al pulsar el botón Start comienza el análisis:


Al terminar nos abrirá una sección en la parte inferior del editor de código fuente con los resultados de las métricas:


Las métricas pueden visualizarse respecto a todo el proyecto o respecto a una unidad, clase, método, etc.

Pongamos por ejemplo este procedimiento:

procedure CrearPedido;
var
Pedido: TPedido;
begin
// Creamos el pedido
Pedido := TPedido.Create;
Pedido.ID := 1;
Pedido.sCliente := 'TRANSPORTES GARCIA, S.L.';

// Añadimos dos artículos al pedido
with Pedido do
begin
Articulos[1] := TArticulo.Create;
Articulos[1].ID := 1;
Articulos[1].sNombre := 'RUEDAS MICHELIN';
Articulos[1].Precio := 410.25;

Articulos[2] := TArticulo.Create;
Articulos[2].ID := 2;
Articulos[2].sNombre := 'FILTRO ACEITE';
Articulos[2].Precio := 176.12;

rTotal := Articulos[1].Precio + Articulos[2].Precio;

// Eliminamos los artículos
Articulos[2].Free;
Articulos[1].Free;
end;

// Eliminamos el pedido
Pedido.Free;
end;

Si examino la métrica del mismo me dirá lo siguiente:

AID (Access of Import Data – Nº de accesos a datos importados ): 0
ALD (Access of Local Data – Nº de accesos a variables de clase ) : 2
CC (Complexity Ciclomatic – Nº de ciclos ): 1
NIC ( Number of Import Classes – Nº de clases importadas): 2
NOLV (Numer of Local Variables – Nº de variables locales): 1

Si queremos averiguar lo que significa cada abreviatura, pulsamos sobre la línea de la métrica con el botón derecho del ratón (en la columna que queremos averiguar) y seleccionamos Show Description:


Nos mostrará una nueva pestaña con el significado de la misma:


También podemos ver una representación gráfica de la métrica pulsando el botón derecho del ratón sobre la rejilla y seleccionando Kiviart Chart:


Abrirá una nueva pestaña en el editor de código fuente mostrando esta gráfica:


Esta herramienta puede ser muy útil para detectar cuellos de botella en el código fuente y evitar desperdicios de memoria declarando más variables que las que necesitamos. Sobre todo viene bien en la programación de videojuegos, donde la velocidad del procesador y el consumo de memoria es crítico para el sistema.

Las métricas que vienen predeterminadas en el IDE pueden modificarse antes de pulsar el botón Start, me modo que nosotros mismos podemos establecer los máximos y mínimos según nuestro criterio.

Al igual que ocurre con las auditorias, las métricas puedes ser realmente desesperantes cuando nuestro proyecto tiene cierta envergadura. No se con tipo de hilo de ejecución han realizado ambos análisis, pero dejan a RAD Studio fuera de juego.

En el próximo artículo veremos las novedades en el lenguaje Object Pascal.

Pruebas realizadas en RAD Studio 2007.

25 julio 2008

Pasando de Delphi 7 a RAD Studio 2007 (3)

EL MODO SYNC EDIT

Si en el artículo anterior vimos como renombrar una variable utilizando las opciones de refactorización, ahora vamos a ver como hacerlo en tiempo real utilizando el modo Sync Edit.

Supongamos que quiero renombrar la variable i de este bloque de código:


Para renombrar rápidamente la variable i hacemos lo siguiente:

1. Seleccionamos todo el procedimiento:


2. Pulsamos el botón Sync Edit Mode que he señalado en la imagen superior y cambiará el color de la selección:


3. Como el cursor ya aparece situado en la variable i sólo tenemos que cambiarle el nombre de i por j:


4. Volvemos a pulsar el botón Sync Edit Mode que ahora está situado al principio del procedimiento y desaparecerá la selección dejando la variable renombrada.

Este modo esta pensado para estos casos en concreto. Si intentamos renombrar por ejemplo una variable de una clase en la interfaz no cambiará en su implementación, para ello ya tenemos que utilizar las opciones de refactorización que vimos en el artículo anterior. El modo Sync Edit sólo afecta al bloque de código seleccionado.

LAS PLANTILLAS DE CÓDIGO: LIVES TEMPLATES

Las plantillas de código nos permiten aumentar la productividad escribiendo código fuente mucho más rápido. Por ejemplo, nos ponemos a crear un bucle for:


Después de escribir la palabra for y pulsar la tecla de espacio nos traerá la plantilla que trae por defecto para la palabra for:


Escribimos la variable i y pulsamos el tabulador, lo que nos llevara al valor inicial:


Como quiero hacer un bucle desde 1 hasta 10, escribo 1 y vuelvo a pulsar el tabulador:


Ahora escribimos el valor 10 y quitamos el valor -1. Pulsamos Intro después de la palabra do nos creará arriba la variable automáticamente:


Cuando nos acostumbramos a este modo de trabajar se obtiene mucha velocidad y comodidad. De todas formas, si queremos escribir por libre sin utilizar la plantilla lo que tenemos que hacer es pulsar la tecla Escape en el momento que aparezca la plantilla. De ese modo vuelve a comportarse como un editor normal.

Igualmente tenemos una platilla para un bucle while:


Otra para la sentencia case:


Para ver todas las plantillas que hay definidas por defecto nos vamos al menú View -> Templates. Aparecerá esta ventana:


Si hacemos doble clic sobre cualquier plantilla la insertará directamente donde tengamos el código fuente:


Para modificar una plantilla pulsamos el botón Edit Code Template:


Se abrirá una nueva pestaña de código en el editor mostrando como está diseñada la plantilla en XML:


Para el bucle for la plantilla se llama fors.xml. Anteriormente hemos pulsado el tabulador para pasar de un punto a otro de la plantilla. Estos puntos están definidos dentro del archivo XML como point.


El valor que hay en hint es la etiqueta de ayuda que va a mostrar al usuario. Lo que voy a hacer yo ahora es predefinir el bucle for a mi gusto, de este modo cuando escriba la palabra for y pulse espacio mostrará esta plantilla:


Una de las plantillas que lleva predefinidas el editor de código y que es muy útil es cuando escribimos begin:


y pulsamos Intro:


Investigando un poco las plantillas estándar XML que hay definidas podemos desarrollar las nuestras propias para ganar velocidad al escribir código.

EL DEPURADOR DE CÓDIGO

El debugger también ha sufrido algunos cambios que van a mejor y que permiten averiguar los errores con mucha más rapidez. Cuando ejecutamos un programa con el botón:


o con la tecla F9, el debugger está activado por defecto, aunque se puede desactivar seleccionado en el menú superior Tools -> Options y en la ventana que aparece seleccionamos a la izquierda la sección Debugger Options y desactivamos la opción Integrated Debugging:


Supongamos que quiero depurar este bloque de código:


Lo que tenemos que hacer es pinchar con el botón izquierdo del ratón en la barra gris que hay a la izquierda del código para que aparezca el punto rojo que marca el inicio de la depuración (como se muestra en la imagen superior).

Cuando ejecutamos el programa y pasa por ese procedimiento se detiene en la línea donde hemos situado el punto de interrupción (el punto rojo):


Aparece una flecha azul en la parte izquierda (encima del punto rojo) que nos va a indicar la línea que se va a ejecutar en el programa. Para avanzar a la siguiente línea debemos pulsar la tecla F8. Los puntos azules que aparecen son las líneas por donde va a pasar el puntero de ejecución.

Al pulsar F8 para pasar a la siguiente línea dejará seleccionada la primera y saltará al bucle for:


La flecha azul se sitúa ahora al comienzo del bucle mientas que en la línea anterior que ha ejecutado nos pone una señal verde de confirmación para indicarnos que se ha ejecutado correctamente.

Como ya ha ejecutado la primera línea, si situamos el cursor del ratón encima de la variable c nos dirá su contenido:


Conforme vamos pulsando F8 y viendo como se va repitiendo el bucle también tenemos a la izquierda del editor de código una sección en la que nos muestra en tiempo real el contenido de las variables locales:


Podemos ver incluso el contenido del array bidimensional que he creado:


Si situamos el cursor encima de Mapa nos muestra todo el contenido del mismo permitiendo incluso observar el contenido de cada dimensión del array:


Este procedimiento que estamos monitorizando no contiene llamadas a otros procedimientos, pero si las tuviera podríamos entrar a los mismos pulsando el botón F7 en lugar de F8.

Podemos salir del modo de depuración con la tecla F9 que lo que hace es continuar la ejecución del programa o podemos detener el programa seleccionando Run -> Program Reset o pulsando la combinación de teclas CTRL + F2.

Veamos otro ejemplo un poco más complicado. Supongamos que tenemos un pedido con una serie de artículos. Lo vamos a controlar con estas dos clases:

type
TArticulo = class
ID: Integer;
sNombre: String;
rPrecio: Real;
end;

TPedido = class
ID: Integer;
sCliente: String;
Articulos: array[1..50] of TArticulo;
rTotal: Real;
end;

Ahora creamos un procedimiento que crea un pedido y le añade artículos:

procedure CrearPedido;
var
Pedido: TPedido;
begin
// Creamos el pedido
Pedido := TPedido.Create;
Pedido.ID := 1;
Pedido.sCliente := 'TRANSPORTES GARCIA, S.L.';

// Añadimos dos artículos al pedido
with Pedido do
begin
Articulos[1] := TArticulo.Create;
Articulos[1].ID := 1;
Articulos[1].sNombre := 'RUEDAS MICHELIN';
Articulos[1].rPrecio := 410.25;

Articulos[2] := TArticulo.Create;
Articulos[2].ID := 2;
Articulos[2].sNombre := 'FILTRO ACEITE';
Articulos[2].rPrecio := 176.12;

rTotal := Articulos[1].rPrecio + Articulos[2].rPrecio;

// Eliminamos los artículos
Articulos[2].Free;
Articulos[1].Free;
end;

// Eliminamos el pedido
Pedido.Free;
end;

Ahora situamos el punto de interrupción en la primera línea y vamos ejecutando el pedido con la tecla F8 hasta ejecutar la línea:

rTotal := Articulos[1].rPrecio + Articulos[2].rPrecio;


Si situamos el cursor encima del objeto Pedido podemos ir rastreando todo su contenido en tiempo real:


De este modo podemos ver la jerarquía de clases de un solo vistazo y sin tener que poner mensajes de depuración para ver su contenido. Esto es muy útil cuando queremos ver el contenido de los registros de las bases de datos que estamos procesando.

Por si esto fuera poco además podemos mirar a la izquierda la jerarquía y contenido de los objetos como si de un explorador se tratase:


Esto da mucha seguridad y potencia al programador para realizar una completa auditoria del código fuente en tiempo real, sobre todo cuando tenemos que modificar el código fuente que ha realizado otro programador y no tenemos ni idea de su comportamiento.

COMPROBANDO LAS PERDIDAS DE MEMORIA

Uno de los grandes problemas que tenemos en el lenguaje Object Pascal es que no libera automáticamente de memoria las variables y objetos que hemos creado (al contrario de otros lenguajes más modernos como Java, C#, Python, Ruby, etc.).

En las versiones de Delphi for Win 32 y RAD Studio tenemos disponible una variable global que permite averiguar que variables no hemos liberado al finalizar la ejecución del programa.

Al inicio de nuestro programa tenemos que activar esta variable:

ReportMemoryLeaksOnShutdown := True;

En el ejemplo del pedido que vimos anteriormente supongamos que no he liberado el artículo 2:

procedure CrearPedido;
var
Pedido: TPedido;
begin
ReportMemoryLeaksOnShutdown := True;

// Creamos el pedido
Pedido := TPedido.Create;
Pedido.ID := 1;
Pedido.sCliente := 'TRANSPORTES GARCIA, S.L.';

// Añadimos dos artículos al pedido
with Pedido do
begin
Articulos[1] := TArticulo.Create;
Articulos[1].ID := 1;
Articulos[1].sNombre := 'RUEDAS MICHELIN';
Articulos[1].rPrecio := 410.25;

Articulos[2] := TArticulo.Create;
Articulos[2].ID := 2;
Articulos[2].sNombre := 'FILTRO ACEITE';
Articulos[2].rPrecio := 176.12;

rTotal := Articulos[1].rPrecio + Articulos[2].rPrecio;

// Eliminamos los artículos
Articulos[2].Free;
//Articulos[1].Free;
end;

// Eliminamos el pedido
Pedido.Free;
end;

Como podéis ver en el código fuente he activado la variable global ReportMemoryLeaksOnShutdown para comprobar las perdidas de memoria.

Al finalizar la ejecución del programa nos aparecerá este mensaje:


Nos dice la clase de objeto que no hemos liberado y los bytes de memoria que se han perdido. El fallo que tiene es que no dice ni en que unidad se ha producido la pérdida de memoria y el número de líneas o procedimiento donde se produjo el error.

Esto es muy útil para zonas localizadas del programa cuando sabemos por donde anda el error, pero no es muy práctico en grandes proyectos donde no sabemos ni siquiera la unidad que está sufriendo las pérdidas de memoria.

Personalmente recomiendo utilizar el experto EurekaLog, que se integra perfectamente en cualquier versión de Delphi y le da cien vueltas al depurador de memoria de CodeGear.

En el próximo artículo seguiremos exprimiendo al máximo RAD Studio 2007.

Pruebas realizadas con RAD Studio 2007.

Publicidad