Diferencia entre revisiones de «Material»

De Wiki del curso de C
Saltar a: navegación, buscar
(Tareas)
 
(No se muestran 18 ediciones intermedias del mismo usuario)
Línea 4: Línea 4:
 
* [[:Archivo:Slides_prin.pdf|Transparencias para imprimir]]  (Se irá actualizando)
 
* [[:Archivo:Slides_prin.pdf|Transparencias para imprimir]]  (Se irá actualizando)
 
* [[:Archivo:plantilla_gol.c|Plantilla Juego de la vida]]
 
* [[:Archivo:plantilla_gol.c|Plantilla Juego de la vida]]
 +
* [https://github.com/profedotc/solucion Repositorio con la solución del trabajo de clase] (se irá actualizando)
 +
* [https://en.cppreference.com/w/c Documentación de C]
 +
* [[:Archivo:Autotools.zip | Plantilla autotools]]
  
 
== Vídeos ==
 
== Vídeos ==
Línea 9: Línea 12:
 
# https://youtu.be/C0UiG1uYgYQ
 
# https://youtu.be/C0UiG1uYgYQ
 
# https://youtu.be/gqzYwGdPfYM
 
# https://youtu.be/gqzYwGdPfYM
 +
# https://youtu.be/IDfllYxE7I4
 +
# https://youtu.be/57-zWEnGxHU
 +
# https://youtu.be/1SJ_fEXD7OA
 +
# https://youtu.be/EhX3E8lLcUM
 +
# No hay
 +
# https://youtu.be/dk-0xF5pJpQ (incompleto)
 +
# https://youtu.be/sq14oKSodPM
 +
# https://youtu.be/JHUozE_4HPs
 +
# https://youtu.be/cVDUGO0_saE
 +
# https://youtu.be/Qq9u5Odq0jU
  
 
== Tareas ==
 
== Tareas ==
Línea 19: Línea 32:
 
|-
 
|-
 
| 1
 
| 1
|  
+
|
 
|  Versión inicial
 
|  Versión inicial
 
* Completada la plantilla del juego de la vida
 
* Completada la plantilla del juego de la vida
Línea 34: Línea 47:
  
 
| 2.2
 
| 2.2
| Intercambiar mundos en lugar de copiarlos
+
| Crear makefile y dividir el programa
* Se utiliza un array de 3 dimensiones para guardar los 2 mundos
+
* Se crean "gol.h" y "gol.c"
* Se van intercambiando los mundos "actual" y "siguiente" en cada iteración
+
* Se crea un makefile para compilar y limpiar el repositorio
* Se elimina la función `gol_copy`, ahora innecesaria
+
* Se añade un .gitignore para ignorar los productos de la compilación
 
|-
 
|-
  
Línea 95: Línea 108:
 
* La memoria para los dos mundos se reserva de una vez. La referencia a este bloque de memoria se guarda en la estructura del objeto
 
* La memoria para los dos mundos se reserva de una vez. La referencia a este bloque de memoria se guarda en la estructura del objeto
 
* Se utiliza la misma lógica que antes para acceder a las células, con dos punteros a los mundos que vamos intercarbiando.
 
* Se utiliza la misma lógica que antes para acceder a las células, con dos punteros a los mundos que vamos intercarbiando.
 +
|-
 +
 +
| rowspan="2" | 7
 +
| 7.1
 +
| Objetos: Reserva dinámica de memoria para la estructura `gol`
 +
* Se cambia ligeramente la interfaz de la función `gol_alloc` para que devuelva un puntero a `struct gol` en lugar de recibirlo.
 +
* Ahora se reservan dinámicamente dos bloques de memoria: uno para la estructura `struct gol` y otro para los arrays del mundo.
 +
* La función `gol_free` debe liberar los dos bloques de memoria.
 +
|-
 +
 +
| 7.2
 +
| Objetos: Ocultar la estructura `gol`
 +
Mediante una declaración adelantada de `struct gol` en `gol.h` conseguimos
 +
ocultar los campos de nuestro objeto (privatizar) a los usuarios de la librería.
 +
|-
 +
 +
| rowspan="2" | 8
 +
| 8.1
 +
| Utilizar getopt para recibir argumentos de línea de comandos
 +
* El programa ahora recibe como argumentos el ancho y el alto del mundo, además de el argumento `-u, --usage` que imprime el siguiente mensaje de ayuda:
 +
Usage: ./gol -w <width> -h <height>
 +
      -u, --usage: Prints this help message
 +
      -w, --width: Width of the world
 +
      -h, --height: Height of the world
 +
* Es obligatorio especificar el tamaño del mundo, si no se especifica se imprime un mensaje de error y se termina el programa.
 +
* Se define el tipo `struct gol_options` para guardar los parámetros de entrada: `int width`, `int height` y `bool usage`
 +
* Se crean dos funciones estáticas que encapsulan la lógica de tratar los argumentos y de imprimir el mensaje de ayuda:
 +
* `static bool parse_args(struct gol_options *gol_opt, int argc, char *argv[]);`
 +
* `static void print_usage(const char *argv0);`
 +
|-
 +
 +
| 8.2
 +
| Implementar la opción de inicializar el mundo aleatoriamente
 +
* Se añade un argumento opcional con un parámetro obligatorio. Indica el patrón de inicialización del mundo: "glider" o "random"
 +
* Se añade otro argumento opcional con parámetro obligatorio. Proporcionará la semilla para `srand()`. Si no se pasa este parámetro, se toma como semilla el tiempo actual. Esta es ahora la salida de `./gol -u`:
 +
Usage: ./gol -w <width> -h <height> [-s <seed>] [-p <pattern>]
 +
      -u, --usage:            Prints this help message
 +
      -w, --width:            Width of the world
 +
      -h, --height:            Height of the world
 +
      -s, --Seed [seed]:      Seed for random world.
 +
      -p, --pattern [pattern]: Initialization pattern:
 +
- glider
 +
- random
 +
* Se modifica el método `gol_init()` para recibir un enumerado, `enum gol_init_pattern`, que esta definido en `gol.h`. El método `gol_init()` llama a los métodos privados `gol_init_rand()` o `gol_init_glider()`, según el patrón de inicialización seleccionado.
 +
* Se crea una función auxiliar en `main.c` que transforma del string recibido como argumento a el enumerado que acepta `gol_init()`
 +
* Se modifica `mem_test.c` para adaptarlo al nuevo `gol_init()`
 +
|-
 +
 +
| rowspan="2" | 9
 +
| 9.1
 +
| Cargar y guardar el mundo desde un archivo binario
 +
* Implementar los métodos `gol_save()` y `gol_load()` para guardar y cargar el mundo desde un archivo binario. Estas funciones simplemente guardan el array tal cual en el archivo especificado y leen el archivo guardándolo en el array.
 +
bool gol_load(struct gol *gol, const char *file);
 +
bool gol_save(const struct gol *gol, const char *file);
 +
* Modificar el main para que acepte los siguientes comandos, además de 'q' para cerrar:
 +
* 'l': Si se pulsa este caracter, llamar a una función estática `load_world()` que pida al usuario que inserte el nombre de un archivo.  Luego carga este archivo con la función `gol_load()`
 +
* 's': Si se pulsa este caracter, llamar a una función estática `save_world()` que pida al usuario que inserte el nombre de un archivo.  Luego guarda este archivo con la función `gol_save()`
 +
* Si cualquiera de los nuevos comandos falla, se informa al usuario del error y se continua la ejecución. Para que el usuario pueda leer el mensaje de error se llama a `getchar()`, así se pausa la ejecución hasta que se pulse un caracter.
 +
|-
 +
 +
| 9.2
 +
| Guardar y cargar el mundo desde un archivo de texto
 +
Modificar `gol_save()` y `gol_load()` para guardar y cargar el estado del
 +
mundo en un formato de texto parecido al que se usa en `gol_print()`.
 +
 +
Ejemplo de archivo para mundo de 10x20:
 +
. . . . . . . . . . . . . . . . . . . .
 +
. . . . . . . . . . . . . . . . . . . .
 +
. . . . . . . . . . . . . . . . . . . .
 +
. . . . . . . . . . . . . . . . . . . .
 +
. . . . . . . . . . . . . . . . . . . .
 +
. . . . . . . . . . . . . . . . . . . .
 +
. . . . . . # . . . . . . . . . . . . .
 +
. . . . . . . # . . . . . . . . . . . .
 +
. . . . . # # # . . . . . . . . . . . .
 +
. . . . . . . . . . . . . . . . . . . .
 +
|-
 +
 +
| 10
 +
|
 +
| Configura compilación con autotools
 +
* Se mueve todo el código a una nueva carpeta `src/`
 +
* Se crean los archivos necesarios para configurar autotools:
 +
** `autogen.sh`
 +
** `configure.ac`
 +
** `makefile.am`
 +
* Se elimina el `makefile` ya que este será generado por autotools
 +
* Se añaden al `.gitignore` todos los archivos autogenerados de autotools
 +
|-
 +
 +
| rowspan="4" | 11
 +
| 11.1
 +
| Objetos II: Implementar el objeto `gol_toroid` hijo de `gol`
 +
En esta primera versión, se implementa una herencia usando punteros a funciones.
 +
 +
* El objeto `gol` contiene dos atributos nuevos, punteros a las funciones que implementarán la funcionalidad de `get_cell()` y `set_cell()`
 +
* Los métodos estáticos `get_cell()` y `set_cell()` de `gol.c` simplemente llaman a los punteros a funciones del objeto.
 +
* Se crea el objeto nuevo en dos archivos: `gol_toroid.c` y `gol_toroid.h`
 +
* La interfaz de este objeto (funciones en `gol_toroid.h`) sólo añade dos métodos: un constructor y un destructor, `gol_toroid_alloc()` y `gol_toroid_free()`)
 +
* En `gol_toroid.c` se implementan este constructor y este destructor, además de las funciones estáticas `get_cell()` y `set_cell()` cuyas direcciones se asignan a los punteros a funcion.
 +
* Las funciones estáticas `get_cell()` y `set_cell()` de `gol_toroid.c` implementan un acceso toroidal al mundo
 +
* Como en `gol_toroid.c` se necesita acceso a la estructura y a los métodos `gol_alloc()` y `gol_free()`, se vuelven a pasar a `gol.h`.
 +
* Se modifica `mem_test` para que haga uso de este nuevo objeto
 +
|-
 +
 +
| 11.2
 +
| Objetos II: Implementar un nuevo objeto `gol_finite`
 +
* Se implementa el objeto `gol_finite` igual que se implementó `gol_toroid`
 +
* Este objeto implementa un mundo con límites, a partir de los cuales todas las células se consideran muertas
 +
* Se modifica el main para que `getopt` interprete un nuevo argumento que seleccionará el tipo de mundo a instanciar.
 +
* Se modifica `mem_test` para que compruebe los dos mundos
 +
|-
 +
 +
| 11.3
 +
| Objetos II: Privatizar la estructura gol y algunos métodos
 +
* Se crea una paraja de archivos `gol_priv.h` y `gol_priv.c` que albergan las definiciones de `struct gol` y de los métodos `gol_alloc()` y `gol_free()`
 +
* El resto de objetos incluyen este `gol_priv.h` para tener acceso a la estrucutra, constructor y destructor. Pero no se incluye en el `main.c`.
 +
|-
 +
 +
| 11.4
 +
| Objetos II: ...
 
|-
 
|-
  

Revisión actual del 19:15 26 jun 2019

Material de clase

Vídeos

  1. https://youtu.be/C0UiG1uYgYQ
  2. https://youtu.be/gqzYwGdPfYM
  3. https://youtu.be/IDfllYxE7I4
  4. https://youtu.be/57-zWEnGxHU
  5. https://youtu.be/1SJ_fEXD7OA
  6. https://youtu.be/EhX3E8lLcUM
  7. No hay
  8. https://youtu.be/dk-0xF5pJpQ (incompleto)
  9. https://youtu.be/sq14oKSodPM
  10. https://youtu.be/JHUozE_4HPs
  11. https://youtu.be/cVDUGO0_saE
  12. https://youtu.be/Qq9u5Odq0jU

Tareas

Tarea Subtarea Descripción
1 Versión inicial
  • Completada la plantilla del juego de la vida
  • Completado README.md
2 2.1 Intercambiar mundos en lugar de copiarlos
  • Se utiliza un array de 3 dimensiones para guardar los 2 mundos
  • Se van intercambiando los mundos "actual" y "siguiente" en cada iteración
  • Se elimina la función `gol_copy`, ahora innecesaria
2.2 Crear makefile y dividir el programa
  • Se crean "gol.h" y "gol.c"
  • Se crea un makefile para compilar y limpiar el repositorio
  • Se añade un .gitignore para ignorar los productos de la compilación
3 3.1 Encapsular el juego de la vida en una estructura
  • Se encapsulan las variables `worlds` y `current_world` en la estructura `gol`
  • La función `gol_step` se encarga ahora de gestionar la variable `curren_world`
3.2 Ocultar todas las funciones que el usuario no necesite
  • Se ocultan `gol_get_cell` y `gol_count_neighbours`
  • Se declaran como `static` y se elimina el prefijo `gol_`
4 4.1 Memoria dinámica I: Añadir objetivos `debug` y `release`

Se añaden dos nuevos objetivos al makefile que modifican los flags de gcc para crear dos ejecutables distintos según convenga.

4.2 Memoria dinámica I: Reserva dinámica de memoria para el mundo
  • Ahora los mundos se reservan dinámicamente con `malloc`
  • La nueva función `gol_alloc` se encarga de la esta reserva y `gol_free` de la liberación
  • Cada mundo es un puntero a un puntero a un boleano, ie un vector de vectores. Se realiza una llamada a malloc para guardar un vector de punteros a filas y luego se realiza otra llamada por cada fila para reservarla.
  • Para guardar la referencia a los dos mundos se utiliza un array de dos punteros. El mundo actual y el próximo siempre están en la misma posición de este array, y en cada iteración se intercambian las direcciones de los dos punteros.
  • Se usa un enumerado para identificar los índices del mundo actual y el siguiente
5 5.1 Memoria dinámica II: Implementar test de memoria
  • Se crea un nuevo archivo `mem_test.c` con otro `main()` que realiza un número finito de iteraciones del juego sin requerir la interacción del usuario, ni imprimir nada por pantalla
  • Se añade un nuevo objetivo (`test`) al makefile que primero compilar el ejecutable `mem_test` con opciones de depuración y con todas las optimizaciones. Después lanzará este ejecutable con valgrind para comprobar fugas de memoria
5.2 Memoria dinámica II: Una sola reserva de memoria por mundo
  • Los mundos son un único puntero a un bloque de memoria dónde está la matriz entera. Se realiza una llama a malloc por mundo.
  • Ahora hay que calcular el offset de cada célula a mano, por lo que los accesos al mundo siempre se hacen a través de `get_cell` y la nueva función `set_cell`
  • Las funciones `set_cell` y `get_cell` reciben el mundo sobre el que actuar (actual o siguiente)
  • Se saca la lógica para comprobar los límites del mundo a la función `fix_coords` para poder utilizarla en `set_cell` y `get_cell` sin repetir código
6 6.1 Memoria dinámica III: Macro funcional para acceder a las células

Utilizar una macro para reutilizar y evitar repetir el mismo código en `get_cell` y `set_cell`

6.2 Memoria dinámica III: Una reserva de memoria para los dos mundos
  • La memoria para los dos mundos se reserva de una vez. La referencia a este bloque de memoria se guarda en la estructura del objeto
  • Se utiliza la misma lógica que antes para acceder a las células, con dos punteros a los mundos que vamos intercarbiando.
7 7.1 Objetos: Reserva dinámica de memoria para la estructura `gol`
  • Se cambia ligeramente la interfaz de la función `gol_alloc` para que devuelva un puntero a `struct gol` en lugar de recibirlo.
  • Ahora se reservan dinámicamente dos bloques de memoria: uno para la estructura `struct gol` y otro para los arrays del mundo.
  • La función `gol_free` debe liberar los dos bloques de memoria.
7.2 Objetos: Ocultar la estructura `gol`

Mediante una declaración adelantada de `struct gol` en `gol.h` conseguimos ocultar los campos de nuestro objeto (privatizar) a los usuarios de la librería.

8 8.1 Utilizar getopt para recibir argumentos de línea de comandos
  • El programa ahora recibe como argumentos el ancho y el alto del mundo, además de el argumento `-u, --usage` que imprime el siguiente mensaje de ayuda:
Usage: ./gol -w <width> -h <height>
     -u, --usage: Prints this help message
     -w, --width: Width of the world
     -h, --height: Height of the world
  • Es obligatorio especificar el tamaño del mundo, si no se especifica se imprime un mensaje de error y se termina el programa.
  • Se define el tipo `struct gol_options` para guardar los parámetros de entrada: `int width`, `int height` y `bool usage`
  • Se crean dos funciones estáticas que encapsulan la lógica de tratar los argumentos y de imprimir el mensaje de ayuda:
  • `static bool parse_args(struct gol_options *gol_opt, int argc, char *argv[]);`
  • `static void print_usage(const char *argv0);`
8.2 Implementar la opción de inicializar el mundo aleatoriamente
  • Se añade un argumento opcional con un parámetro obligatorio. Indica el patrón de inicialización del mundo: "glider" o "random"
  • Se añade otro argumento opcional con parámetro obligatorio. Proporcionará la semilla para `srand()`. Si no se pasa este parámetro, se toma como semilla el tiempo actual. Esta es ahora la salida de `./gol -u`:
Usage: ./gol -w <width> -h <height> [-s <seed>] [-p <pattern>]
     -u, --usage:             Prints this help message
     -w, --width:             Width of the world
     -h, --height:            Height of the world
     -s, --Seed [seed]:       Seed for random world.
     -p, --pattern [pattern]: Initialization pattern:
				 - glider
				 - random
  • Se modifica el método `gol_init()` para recibir un enumerado, `enum gol_init_pattern`, que esta definido en `gol.h`. El método `gol_init()` llama a los métodos privados `gol_init_rand()` o `gol_init_glider()`, según el patrón de inicialización seleccionado.
  • Se crea una función auxiliar en `main.c` que transforma del string recibido como argumento a el enumerado que acepta `gol_init()`
  • Se modifica `mem_test.c` para adaptarlo al nuevo `gol_init()`
9 9.1 Cargar y guardar el mundo desde un archivo binario
  • Implementar los métodos `gol_save()` y `gol_load()` para guardar y cargar el mundo desde un archivo binario. Estas funciones simplemente guardan el array tal cual en el archivo especificado y leen el archivo guardándolo en el array.
bool gol_load(struct gol *gol, const char *file);
bool gol_save(const struct gol *gol, const char *file);
  • Modificar el main para que acepte los siguientes comandos, además de 'q' para cerrar:
  • 'l': Si se pulsa este caracter, llamar a una función estática `load_world()` que pida al usuario que inserte el nombre de un archivo. Luego carga este archivo con la función `gol_load()`
  • 's': Si se pulsa este caracter, llamar a una función estática `save_world()` que pida al usuario que inserte el nombre de un archivo. Luego guarda este archivo con la función `gol_save()`
  • Si cualquiera de los nuevos comandos falla, se informa al usuario del error y se continua la ejecución. Para que el usuario pueda leer el mensaje de error se llama a `getchar()`, así se pausa la ejecución hasta que se pulse un caracter.
9.2 Guardar y cargar el mundo desde un archivo de texto

Modificar `gol_save()` y `gol_load()` para guardar y cargar el estado del mundo en un formato de texto parecido al que se usa en `gol_print()`.

Ejemplo de archivo para mundo de 10x20:

. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . # . . . . . . . . . . . . .
. . . . . . . # . . . . . . . . . . . .
. . . . . # # # . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
10 Configura compilación con autotools
  • Se mueve todo el código a una nueva carpeta `src/`
  • Se crean los archivos necesarios para configurar autotools:
    • `autogen.sh`
    • `configure.ac`
    • `makefile.am`
  • Se elimina el `makefile` ya que este será generado por autotools
  • Se añaden al `.gitignore` todos los archivos autogenerados de autotools
11 11.1 Objetos II: Implementar el objeto `gol_toroid` hijo de `gol`

En esta primera versión, se implementa una herencia usando punteros a funciones.

  • El objeto `gol` contiene dos atributos nuevos, punteros a las funciones que implementarán la funcionalidad de `get_cell()` y `set_cell()`
  • Los métodos estáticos `get_cell()` y `set_cell()` de `gol.c` simplemente llaman a los punteros a funciones del objeto.
  • Se crea el objeto nuevo en dos archivos: `gol_toroid.c` y `gol_toroid.h`
  • La interfaz de este objeto (funciones en `gol_toroid.h`) sólo añade dos métodos: un constructor y un destructor, `gol_toroid_alloc()` y `gol_toroid_free()`)
  • En `gol_toroid.c` se implementan este constructor y este destructor, además de las funciones estáticas `get_cell()` y `set_cell()` cuyas direcciones se asignan a los punteros a funcion.
  • Las funciones estáticas `get_cell()` y `set_cell()` de `gol_toroid.c` implementan un acceso toroidal al mundo
  • Como en `gol_toroid.c` se necesita acceso a la estructura y a los métodos `gol_alloc()` y `gol_free()`, se vuelven a pasar a `gol.h`.
  • Se modifica `mem_test` para que haga uso de este nuevo objeto
11.2 Objetos II: Implementar un nuevo objeto `gol_finite`
  • Se implementa el objeto `gol_finite` igual que se implementó `gol_toroid`
  • Este objeto implementa un mundo con límites, a partir de los cuales todas las células se consideran muertas
  • Se modifica el main para que `getopt` interprete un nuevo argumento que seleccionará el tipo de mundo a instanciar.
  • Se modifica `mem_test` para que compruebe los dos mundos
11.3 Objetos II: Privatizar la estructura gol y algunos métodos
  • Se crea una paraja de archivos `gol_priv.h` y `gol_priv.c` que albergan las definiciones de `struct gol` y de los métodos `gol_alloc()` y `gol_free()`
  • El resto de objetos incluyen este `gol_priv.h` para tener acceso a la estrucutra, constructor y destructor. Pero no se incluye en el `main.c`.
11.4 Objetos II: ...
... ... ...