AnteriorPosterior

Tema 12: Creación de unidades

  Curso: Curso de Pascal, por Nacho Cabanes

Curso de Pascal. Tema 12: Creación de unidades.

Comentamos en el tema 10 que en muchos lenguajes de programación podemos manejar una serie de bibliotecas externas (en ingles, library) de funciones y procedimientos, que nos permitían ampliar el lenguaje base.

En Turbo Pascal, estas bibliotecas reciben el nombre de "unidades" (unit), y existen a partir de la versión 5. También existen en otras versiones de Pascal recientes, como Free Pascal.

En su momentos, empleamos la unidad CRT, que nos daba una serie de facilidades para manejar la pantalla en modo texto, el teclado y la generación de sonidos sencillos.

Iremos viendo otras unidades estándar cuando accedamos a la pantalla en modo gráfico, a los servicios del sistema operativo, etc. Pero hoy vamos a ver cómo podemos crear las nuestras propias.

¿Para qué? Nos podría bastar con teclear en un programa todas las funciones que nos interesen. Si creamos otro programa que las necesite, pues las copiamos también en ese y ya está, ¿no?

¡ NO ! Las unidades nos ayudan a conseguir dos cosas:

  • La primera es que los programas sean más modulares. Que podamos dejar aparte las funciones que se encargan de batallar con el teclado, por ejemplo, y en nuestro programa principal sólo esté lo que realmente tenga este programa que lo diferencie de los otros. Esto facilita la legibilidad y con ello las posibles correcciones o ampliaciones.
  • La segunda ventaja es que no tenemos distintas versiones de los mismos procedimientos o funciones. Esto ayuda a ganar espacio en el disco duro, pero eso es lo menos importante. Lo realmente interesante es que si se nos ocurre una mejora para un procedimiento, todos los programas que lo usen se van a beneficiar de él automáticamente.


Me explico: imaginemos que estamos haciendo un programa de rotación de objetos en tres dimensiones. Creamos nuestra biblioteca de funciones, y la aprovechamos para todos los proyectos que vayamos a hacer en tres dimensiones. No solo evitamos reescribir en cada programa el procedimento RotaPunto, p.ej.., que ahora se tomará de nuestra unidad "MiGraf3D", sino que si descubrimos una forma más rápida de rotarlos, todos los programas que utilicen el procedimiento RotaPunto se verán beneficiados sólo con recompilarlos.

Pero vamos a lo práctico...

Una "unit" tiene dos partes: una pública, que es aquella a la que podremos acceder, y una privada, que es el desarrollo detallado de esa parte pública, y a esta parte no se puede acceder desde otros programas.

La parte pública se denota con la palabra "interface", y la privada con "implementation".

Debajo de interface basta indicar los nombres de los procedimientos que queremos "exportar", así como las variables, si nos interesase crear alguna. Debajo de implementation escribimos realmente estos procedimientos o funciones, tal como haríamos en un programa normal.

Veamos un ejemplito para que se entienda mejor.

Nota: este ejemplo NO SE PUEDE EJECUTAR. Recordemos que una Unit es algo auxiliar, una biblioteca de funciones y procedimientos que nosotros utilizaremos DESDE OTROS PROGRAMAS. Después de este ejemplo de Unit incluyo un ejemplo de programa cortito que la emplee.

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Unidad que "mejora"   }
 {    la CRT                }
 {    MICRT1.PAS            }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Tmt Pascal Lt 1.20  }
 {--------------------------}
 unit miCrt1;
 
 interface                 { Parte "pública", que se exporta }
 procedure AtXY( X, Y: byte ; texto: string );
                           { Escribe un texto en ciertas coordenadas }
 
 implementation            { Parte "privada", detallada }
 uses crt;                 { Usa a su vez la unidad CRT }
 procedure AtXY( X, Y: byte ; texto: string );
 begin
   gotoXY( X, Y);          { Va a la posición adecuada }
   write( texto );
 end;
 end.                      { Final de la unidad } 
 

Este ejemplo declara un procedimiento "AtXY" que hace un GotoXY y un Write en un solo paso.

Un programa que lo emplease podría ser simplemente:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Programa que usa la   }
 {    unit "MICRT1"         }
 {    PMICRT1.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Tmt Pascal Lt 1.20  }
 {--------------------------}
 program PruebaDeMiCrt1;
 
 uses miCrt1;
 
 begin
   AtXY( 7, 5, 'Texto en la posición 7,5.' );
 end. 
 

Este programa no necesita llamar a la unidad CRT original, sino que nuestra unidad ya lo hace por él.

Ahora vamos a mejorar ligeramente nuestra unidad, añadiéndole un procedimiento "pausa":

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Unidad que mejora la  }
 {    CRT (segunda versión) }
 {    MICRT2.PAS            }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Tmt Pascal Lt 1.20  }
 {--------------------------}
 unit miCrt2;               { Unidad que "mejora más" la CRT }
 
 {-------------------}
 interface                  { Parte "pública", que se exporta }
 
 procedure AtXY( X, Y: byte ; texto: string );
 procedure Pausa;
 
 {-------------------}
 implementation             { Parte "privada", detallada }
 
 uses crt;                  { Usa a su vez la unidad CRT }
 
 var tecla: char;           { variable privada: el usuario no
                              puede utilizarla }
 
 procedure AtXY( X, Y: byte ; texto: string );
 begin
   gotoXY( X, Y);           { Va a la posición adecuada }
   write( texto );
 end;
 
 procedure Pausa;           { Pausa, llamando a ReadKey }
 begin
   tecla := ReadKey;        { El valor de "tecla" se pierde }
 end;
 
 {-------------------}
 end.                       { Final de la unidad }
 
 


Un programa que usase esta unidad, junto con la CRT original podría ser:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Prueba de la unidad   }
 {    MICRT2                }
 {    PMICRT2.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Tmt Pascal Lt 1.20  }
 {--------------------------}
 program PruebaDeMiCrt2;
 uses crt, miCrt2;
 begin
   ClrScr;                                      { De Crt }
   atXY( 7, 5, 'Texto en la posición 7,5.' );   { de miCrt2 }
   pausa;                                       { de miCrt2 }
 end.
 



Finalmente, hay que destacar que las unidades pueden contener más cosas además de funciones y procedimientos: pueden tener un "trozo de programa", su código de inicialización, como por ejemplo:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Unidad que mejora la  }
 {    CRT (tercera versión) }
 {    MICRT3.PAS            }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Tmt Pascal Lt 1.20  }
 {--------------------------}
 unit miCrt3;               { Unidad que "mejora más" la CRT }
 
 {-------------------}
 interface                  { Parte "pública", que se exporta }
 
 var EraMono: boolean;      { Variable pública, el usuario puede
                              acceder a ella }
 procedure AtXY( X, Y: byte ; texto: string );
 procedure Pausa;
 
 {-------------------}
 implementation             { Parte "privada", detallada }
 
 uses crt;                  { Usa a su vez la unidad CRT }
 var tecla: char;           { variable privada: el usuario no
                              puede utilizarla }
 
 procedure AtXY( X, Y: byte ; texto: string );
 begin
   gotoXY( X, Y);           { Va a la posición adecuada }
   write( texto );
 end;
 
 procedure Pausa;           { Pausa, llamando a ReadKey }
 begin
   tecla := ReadKey;        { El valor de "tecla" se pierde }
 end;
 
 {-------------------}      { Aquí va la inicialización }
 begin
   if lastmode = 7          { Si el modo de pantalla era monocromo }
     then EraMono := true   { EraMono será verdadero }
   else EraMono := false;   { si no => falso }
 end.                       { Final de la unidad }
 
 


y el programa podría usar la variable EraMono sin declararla:

 {--------------------------}
 {  Ejemplo en Pascal:      }
 {                          }
 {    Prueba de la unidad   }
 {    MICRT3                }
 {    PMICRT3.PAS           }
 {                          }
 {  Este fuente procede de  }
 {  CUPAS, curso de Pascal  }
 {  por Nacho Cabanes       }
 {                          }
 {  Comprobado con:         }
 {    - Free Pascal 2.2.0w  }
 {    - Turbo Pascal 7.0    }
 {    - Tmt Pascal Lt 1.20  }
 {--------------------------}
 program PruebaDeMiCrt3;
 uses crt, miCrt3;
 begin
   ClrScr;                                      { De Crt }
   atXY( 7, 5, 'Texto en la posición 7,5.' );   { de miCrt3 }
   if not EraMono then
     atXY ( 10, 10, 'Modo de color ' );
   pausa;                                       { de miCrt3 }
 end.
 

Se podría hablar mucho más sobre las unidades, pero intentaré ser breve:

  • Al compilar una unidad se crea un fichero con extensión .TPU (.PPU para Free Pascal), al que se puede acceder desde nuestros programas con dos condiciones: que empleemos la misma versión de compilador (el formato de estos ficheros variaba en cada versión de Turbo Pascal, y quizá también entre versiones de Free Pascal), y que sepamos cómo es la parte pública (interface).
Nota: Por eso mucha gente distribuía sus bibliotecas de rutinas Pascal en forma de TPU: se podía usar las facilidades que nos daban (si teníamos la misma versión de Pascal), pero como no teníamos disponible el fuente, no podíamos modificarlo ni redistribuirlo con nuestro nombre, por ejemplo. Hoy en día, se tiende más a ceder todo el código fuente, y pedir a los usuarios que conserven el copyright y/o envíen al autor las mejoras que propongan.

  • En Turbo Pascal 7 para MsDos, cada unidad tiene su propio segmento de código (esto va para quien conozca la estructura de la memoria en los PC), así que cada unidad puede almacenar hasta 64k de procedimientos o funciones. Los datos son comunes a todas las unidades, con la limitación 64k en total (un segmento) para todos los datos (estáticos) de todo el programa. Si queremos almacenar datos de más de 64k en el programa, tenga una o más unidades, deberemos emplear variables dinámicas, distintas en su manejo de las que hemos visto hasta ahora (estáticas), pero eso ya lo veremos el próximo día... :-)

Esta vez no propongo ejercicios. Que cada uno se construya las units que quiera, como quiera, y vaya consultando dudas...

Actualizado el: 08-07-2012 12:31

AnteriorPosterior