Laboratorio de Programación Orientada a Objetos (Grupo C) Miguel Angel Mendoza Moreno 1 LABORATORIO 1: Proyectos (Makefile) Objetivo. Conceptuar e implementar aplicaciones claramente modularizadas y controladas por el programador. CONCEPTUALIZACIÓN MODULARIZACIÓN. La modulación es un concepto que se ha trabajado en la formación académica desde el primer semestre, por esos hay que verla desde dos perspectivas básicas: - - La estimación de reducir un gran problema en pequeños segmentos bien definidos, con el respectivo refinamiento por pasos, esto es la aplicación de funciones y procedimientos (recordemos que C solo admite funciones). La construcción de bloques de abstracciones altamente cohesivos y débilmente acoplados por medio de la implementación de clases, identificando una interfaz y una implementación. ¿Por qué modularizar en archivos fuente separados? Profundizar en: http://www-etsi2.ugr.es/depar/ccia/mp2/old/apoyo/comsep/comsep.html Dado que la organización de los archivos conlleva a una separación adecuada de todo el código, se tiene que: - Un archivo nombre.h donde se incluye el tipo abstracto de dato a manejar (más adelante veremos que una clase es la representación de un Tad), junto con la información administrativa (Comentarios que incluyen el nombre del autor, descripción de la aplicación, fecha, asignatura y plataforma operativa). - Un archivo nombre.cpp que implementa cada uno de las funciones miembro definidas dentro de la(s) clase(s) del archivo .h. - Un archivo principa.cpp que posee la función main() y que se restringe a solicitar “servicios” a los archivos complementarios. La asociación de estos tres archivos se denomina Proyecto y se entenderá que el header (nombre.h) denotas las restricciones básicas para poder invocar cada uno de los métodos implementados en nombre.cpp, mientras principa.cpp representará las solicitudes que haga el cliente. Ahora se evidencia un programa dividido en tres segmentos, ¿qué se debe hacer para unificarlo y que corra adecuadamente?, para responder este interrogante, deberemos revisar si deseamos una compilación y enlazamiento desde un IDE (como Turbo C++ o Rhide) o desde la interfaz de línea de comandos. Compilación y enlazamiento desde: - IDE –Entorno de Desarrollo Integrado- (Como turbo C++, DevC++,Rhide): Se direcciona el directorio al que posee los tres archivos, luego en el menú Proyecto (Project) se crea un nuevo proyecto (nombre.prj) y se adicionan cada uno de los archivos con extensión .cpp. De tal manera que al producir la compilación sobre cualquiera de los tres archivos fuente, se estará produciendo la compilación común de los tres pues ya se encuentran enlazados (linked). Si se hacen las modificaciones sobre alguno de los tres archivos, la opción Rebuild All (Reconstruir todo) permitirá recompilar y enlazar nuevamente los archivos del proyecto. - Interfaz de Línea de Comandos: Hay que garantizar que todos los archivos fuente se encuentren en la misma ruta (obviamente en el mismo directorio preferiblemente), luego brindar el siguiente comando: Laboratorio de Programación Orientada a Objetos (Grupo C) Miguel Angel Mendoza Moreno 2 g++ -c -g -Wno-deprecated nombre.cpp principa.cpp donde: g++ es el comando de compilación de C++. -c Realiza solamente el preprocesamiento y la compilación de los archivos fuente. No se lleva a cabo la etapa de enlazado. -g Incluirá en el ejecutable la información necesaria para poder trazarlo empleando un depurador. -Wno –deprecated No despliega aquellos warnings derivadas de la desaprobación del archivo .h particularmente. Luego: g++ -o nombresalida principa.o nombre.o -o especifica el nombre del archivo de salida, resultado de la tarea solicitada al compilador, al compilar se procede al enlazamiento y generación de la salida. ¿Como largo el proceso?, pues reduzcámoslo con la creación de un makefile!. MAKEFILE Profundicemos la temática en: http://decsai.ugr.es/~jmbs/MP2/Introgcc_2003.pdf La utilidad make proporciona los mecanismos adecuados para la gestión de proyectos software, makefile contiene las especificaciones necesarios para la operación de make. En una visión simple, el uso de make conjuntamente con los archivos makefile proporciona el mecanismo para realizar una gestión inteligente, sencilla y precisa de un proyecto software, ya que permite: a) Una forma sencilla de especificar la dependencia entre los módulos de un proyecto software, b) La recompilación únicamente de los módulos que han de actualizarse, c) Obtener siempre la versión última que refleja las modificaciones realizadas, y d) Un mecanismo casi estándar de gestión de proyectos software, independiente de la plataforma en la que se desarrolla. Los elementos con que cuenta un archivo makefile (note que no tiene extensión) son: Comentarios, Reglas explícitas, Órdenes y Destinos simbólicos. Comentarios: Tienen como objeto clarificar el contenido del archivo makefile. Una línea del comentario tiene en su primera columna el símbolo #. Los comentarios tienen el ámbito de una línea, que no será contada dentro del proceso de reconocimiento de sentencias propias de makefile. Reglas: Constituyen el mecanismo por el que se indica a make los destinos, las listas de dependencias y cómo construir los destinos. Como puede deducirse, son la parte fundamental de un archivo makefile. Las reglas que instruyen a make son de dos tipos: explícitas e implícitas y se definen de la siguiente forma: Las reglas explícitas dan instrucciones a make para que construya los archivos especificados. Las reglas implícitas dan instrucciones generales que make sigue cuando no puede encontrar una regla explícita. Las reglas tienen este formato general: Línea de dependencia orden(es) Laboratorio de Programación Orientada a Objetos (Grupo C) Miguel Angel Mendoza Moreno 3 La línea de dependencia es diferente para las reglas explícitas e implícitas, pero las instrucciones aplicables son las mismas. El formato habitual de una regla explícita es el siguiente: destino: lista de dependencia ordenes(es) donde: destino especifica el archivo a crear. lista de dependencia especifica los archivos de los que depende destino. La lista se especifica separando los nombres de los archivos con espacios en blanco. Si alguno de los archivos especificados en esta lista se ha modificado, se busca una regla que contenga a ese archivo como destino y se construye. Una vez se han construido las últimas versiones de los archivos especificados en lista de dependencia se construye destino. órden(es) son órdenes válidas para el sistema operativo en el que se ejecute la utilidad make (en nuestro caso serán habitualmente llamadas al compilador en línea g++). Pueden incluirse cuantas instrucciones se requieran como parte de una regla, cada uno en una línea distinta. Usualmente estas instrucciones sirven para construir el destino, aunque no tiene porque ser así. MUY IMPORTANTE: Cada línea de órdenes empezará con un TABULADOR. Si no es así, make mostrará un error y no continuará procesando el archivo makefile. Ordenes: Se puede incluir cualquier orden válida del sistema operativo en el que se ejecute la utilidad make. Pueden incluirse cuantas órdenes se requieran como parte de una regla, cada una en una línea distinta. Las órdenes pueden ir precedidas por prefijos como: @ Desactivar el eco durante la ejecución de esa orden. - Ignorar los errores que puede producir la orden a la que precede. Destinos simbólicos: Un destino simbólico se especifica en un fichero makefile en la primera línea operativa del mismo. En su sintaxis se asemeja a la especificación de una regla, con la diferencia que no tiene asociada alguna orden. El formato es el siguiente: destino simbólico: lista de destinos donde: destino simbólico es el nombre del destino simbólico. El nombre particular no tiene alguna importancia. lista de destinos especifica los destinos que se construirán cuando se invoque a make. La finalidad de incluir un destino simbólico en un fichero makefile es la de que se construyan varios destinos sin necesidad de invocar a make tantas veces como destinos se desee construir. Al estar en la primera línea operativa del fichero makefile, la utilidad make intentará construir el destino simbólico. Para ello, examinará la lista de dependencia (llamada ahora lista de destinos) y construirá cada uno de los destinos de esta lista: debe existir una regla para cada uno de los destinos. Laboratorio de Programación Orientada a Objetos (Grupo C) Miguel Angel Mendoza Moreno 4 Funcionamiento de make. El funcionamiento de la utilidad make es el siguiente: 1. En primer lugar, busca el archivo makefile que debe interpretar. Si se ha especificado la opción -f archivo, busca ese archivo. Si no, busca en el directorio actual un archivo llamado makefile ó Makefile. En cualquier caso, si lo encuentra, lo interpreta; si no, da un mensaje de error y termina. 2. Intenta construir el(los) destino(s) especificado(s). Si no se proporciona algún destino, intenta construir solamente el primer destino que aparece en el archivo makefile. Para construir un destino es posible que deba construir antes otros destinos: el destino especificado depende de otros que no están construidos. Si es así, los construye examinando las listas de dependencias. Esta reacción en cadena se llama dependencia encadenada. 3. Si en cualquier paso falla al construir algún destino, se detiene la ejecución, muestra un mensaje de error y borra el destino que estaba construyendo. las siguientes órdenes: % make % make -f makefile % make saludo.exe % make -f makefile saludo.exe tienen el mismo efecto. En los dos últimos casos se ha especificado el destino a crear, aunque no hace falta ya que éste es el primero del archivo makefile. ¿Qué ocurre cuando se dice que se compila y se enlaza? Gráficamente: Laboratorio de Programación Orientada a Objetos (Grupo C) Miguel Angel Mendoza Moreno 5 APLICACIÓN Construyamos unos cuantos ejemplos donde sea posible verificar lo aprendido: Recordando nuestro pasado laboratorio, construyamos un Proyecto (para nuestro caso, los archivos fuente separados o módulos) para producir la clasificación de 20 estudiantes como aprobados o reprobados, cada estudiante posee un nombre, código, fecha de nacimiento y nota final. Trabajémoslo con Registros (struct) –dado que aún no conocemos el concepto de clases (class)-. A partir de la implementación que posee cada estudiante: Ejemplo1: (Compilación simple) 1. Cree el directorio Ejemplo1 dentro de la carpeta /home/sistemas/labpooC para trabajar los siguientes archivos. 2. Edite un archivo estudiante.h y en este: o Posicione la información administrativa o Defina el registro estudiante (struct estudiante) o Especifique los prototipos de las funciones a emplear (lectura, calcular, visualizar) 3. Edite un archivo estudiante.cpp y en este implemente cada una de las funciones definidas en el punto anterior. Su primera línea debe ser #include “estudiante.h” 4. Edite un archivo principa.cpp que posea únicamente la función main() y su respectivo cuerpo. Su primera línea debe ser #include “estudiante.h” 5. Habiendo salvado los anteriores archivos (obviamente), ahora probemos una primera forma de compilación y enlazamiento: - En la línea de comando escriba gcc -c -g -Wno-deprecated estudiante.c principa.c - No debieron salir errores, si fue así, corrijamos e intente nuevamente. - Ahora escriba gcc -o estudiante.exe principa.o estudiante.o 6. De realizarse el procedimiento exitosamente, ahora podemos ejecutar el archivo de salida denominado estudiante.exe, para ello escriba ./estudiante.exe (y pulse enter) para que funcione su programa. 7. De esta manera hemos compilado de una primera forma (la más larga!!!) 8. Una forma de haber logrado la compilación y enlace de manera más reducida sería: gcc –o estudiante.exe estudiante.c principa.c Ejemplo2: (Un makefile básico) 1. Cree el directorio Ejemplo2 dentro de la carpeta /home/sistemas/labpooC para trabajar los siguientes archivos. 2. Copie a la Carpeta Ejemplo2 los archivos estudiante.h, estudiante.c, principa.c. 3. Edite un archivo makefile y en este digite (respete los tabuladores): estudiante.exe: estudiante.c principa.c @echo Hola mundo, estoy ejecutando mi primer makefile … gcc -o estudiante.exe: estudiante.c principa.c 4. 5. 6. 7. 8. 9. Guarde los cambios en el archivo Escriba en la interfaz de línea de comando el comando make (y pulse enter). Verifique la no existencia de errores, si es así repita los pasos 3, 4, 5. Verifique los archivos que se encuentran el nuestro directorio Observe que ya cuenta con el ejecutable (estudiante.exe) Ejecute el archivo Ejemplo3: (Makefile avanzado I) 1. Cree el directorio Ejemplo3 dentro de la carpeta /home/sistemas/labpooC para trabajar los siguientes archivos. Laboratorio de Programación Orientada a Objetos (Grupo C) Miguel Angel Mendoza Moreno 6 2. Copie a la Carpeta Ejemplo3 los archivos estudiante.h, estudiante.c, principa.c. 3. Edite un archivo makefile y en este digite (respete los tabuladores): procesos: ejem1.exe mueveexe estudiante.exe: estudiante.c principa.c @echo Hola mundo, estoy ejecutando mi segundo makefile … gcc -o estudiante.exe: estudiante.c principa.c mueveexe: @echo Creando directorio de copia... mkdir DirectorioCopia @echo Copiando los .exe a DirectorioCopia... cp ejem1.exe DirectorioCopia/ @Listo papa, Mendoza copió el ejecutable... 4. 5. 6. 7. 8. 9. Guarde los cambios en el archivo Escriba en la interfaz de línea de comando el comando make (y pulse enter). Verifique la no existencia de errores, si es así repita los pasos 3, 4, 5. Verifique los archivos que se encuentran el nuestro directorio Observe que ya cuenta con el ejecutable (estudiante.exe) Ejecute el archivo Ejemplo4: (Makefile avanzado II) 1. Cree el directorio Ejemplo4 dentro de la carpeta /home/sistemas/labpooC para trabajar los siguientes archivos. 2. Copie a la Carpeta Ejemplo4 los archivos estudiante.h, estudiante.c, principa.c. 3. Edite un archivo makefile y en este digite (respete los tabuladores): #Archivo makefile1 #Para compilar otros archivos modifique el contenido de FTES, OBJS, CAB, #EJEC FTES = estudiante.c principa.c OBJS = principa.o estudiante.o CAB = cadena.h EJEC = estudiante.exe #Banderas CC = gcc CFLAGS = -g -Wno-deprecated #Comandos para compilación #El ejecutable depende de los .o $(EJEC):$(OBJS) $(CC) -o $@ $(OBJS) #Los .o dependen de los archivos .h y .c @echo Lab POO GrupoC MIGUEL MENDOZA $(OBJS): $(CAB) $(FTES) $(CC) -c $(CFLAGS) $(FTES) clean: @echo creando carpeta destino Laboratorio de Programación Orientada a Objetos (Grupo C) Miguel Angel Mendoza Moreno 7 mkdir Carpeta @echo moviendo toda la información a directorio Carpeta... cp *.c ./Carpeta cp *.h ./Carpeta cp makefile ./Carpeta @echo borrando puntos o... rm -i -f *.o # end 4. 5. 6. 7. 8. 9. Guarde los cambios en el archivo Escriba en la interfaz de línea de comando el comando make clean (y pulse enter). Verifique la no existencia de errores, si es así repita los pasos 3, 4, 5. Verifique los archivos que se encuentran el nuestro directorio Observe que ya cuenta con el ejecutable (estudiante.exe) Ejecute el archivo APLICACIÓN Dado el siguiente problema, realice la respectiva implementación empleando registros (strcuts) y estableciendo claramente los módulos y funciones propias. Genere el respectivo makefile en la carpeta Aplicación. En un hotel se procesa la información sobre las habitaciones y huéspedes en los siguientes arreglos de registros: Habitaciones Num Tipo Precio disp …. Num tipo Precio disp Donde: HABI[i] representa al registro que contiene información de la habitación i. Los campos del registro son los siguientes: Num: representa el número de habitación Tipo: Indica el tipo de habitación. Se ingresa: SI: si la habitación es simple, DO: si la habitación es doble, TR: si la habitación es triple, SU: si la habitación es suite. Precio: Representa el precio de la habitación. Disp: Indica si la habitación esta disponible o no. Se ingresa: SI: Si la habitación esta disponible. NO: Si la habitación no esta disponible. Nota: El arreglo está ordenado teniendo en cuenta el número de la habitación. Huéspedes Num nom Fecarr ….. Num nom Fecarr HUES[I] representa al registro que contiene la información del huésped i. Los campos del registro son los siguientes: Num: Indica el número de habitación. Nom: expresa el nombre del huésped. Fecarr: Indica la fecha del arribo del cliente. Construya un algoritmo en el que se pueda efectuar las siguientes operaciones: a. Leer la información de las habitaciones. b. Dado el nombre de un huésped y la fecha actual, regrese lo que debe pagar dicho huésped (asuma que la diferencia entre las fechas la da número de días de alojamiento y que cada mes tiene 30 días). Laboratorio de Programación Orientada a Objetos (Grupo C) Miguel Angel Mendoza Moreno c. d. e. 8 Determine el número de habitaciones simples, dobles, triples y suites que hay disponibles. Dado el nombre de un huésped, lo elimine del arreglo correspondiente y se libere su habitación. Dado el nombre de un huésped que desea una habitación tipo X, ésta le sea asignada siempre que esté disponible, haciendo la actualización en los arreglos correspondientes. *** Analice los resultados y las implicaciones de lo programado!!! En este nivel Usted ya está en capacidad de compilar cualquier tipo de proyecto seleccionando la forma que considere más adecuada a su gusto.