Diferencia entre revisiones de «Material»
De Wiki del curso de C
(Página creada con « == Material de clase == * Transparencias (Se irá actualizando) * Plantilla Juego de la vida == Vídeos ==...») |
(→Tareas) |
||
(No se muestran 21 ediciones intermedias del mismo usuario) | |||
Línea 1: | Línea 1: | ||
− | |||
== Material de clase == | == Material de clase == | ||
* [[:Archivo:Transparencias.pdf|Transparencias]] (Se irá actualizando) | * [[:Archivo:Transparencias.pdf|Transparencias]] (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 == | ||
+ | |||
+ | |||
+ | {| class="wikitable" | ||
+ | ! Tarea | ||
+ | ! Subtarea | ||
+ | ! Descripción | ||
+ | |- | ||
+ | | 1 | ||
+ | | | ||
+ | | Versión inicial | ||
+ | * Completada la plantilla del juego de la vida | ||
+ | * Completado README.md | ||
+ | |- | ||
+ | |||
+ | | rowspan="2" | 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 | ||
+ | |- | ||
+ | |||
+ | | rowspan="2" | 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_` | ||
+ | |- | ||
+ | |||
+ | | rowspan="2" | 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 | ||
+ | |- | ||
+ | |||
+ | | rowspan="2" | 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 | ||
+ | |- | ||
+ | |||
+ | | rowspan="2" | 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. | ||
+ | |- | ||
+ | |||
+ | | 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
- Transparencias (Se irá actualizando)
- Transparencias para imprimir (Se irá actualizando)
- Plantilla Juego de la vida
- Repositorio con la solución del trabajo de clase (se irá actualizando)
- Documentación de C
- Plantilla autotools
Vídeos
- https://youtu.be/C0UiG1uYgYQ
- 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
Tarea | Subtarea | Descripción |
---|---|---|
1 | Versión inicial
| |
2 | 2.1 | Intercambiar mundos en lugar de copiarlos
|
2.2 | Crear makefile y dividir el programa
| |
3 | 3.1 | Encapsular el juego de la vida en una estructura
|
3.2 | Ocultar todas las funciones que el usuario no necesite
| |
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
| |
5 | 5.1 | Memoria dinámica II: Implementar test de memoria
|
5.2 | Memoria dinámica II: Una sola reserva de memoria por mundo
| |
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
| |
7 | 7.1 | Objetos: Reserva dinámica de memoria para la estructura `gol`
|
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
Usage: ./gol -w <width> -h <height> -u, --usage: Prints this help message -w, --width: Width of the world -h, --height: Height of the world
|
8.2 | Implementar la opción de inicializar el mundo aleatoriamente
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
| |
9 | 9.1 | Cargar y guardar el mundo desde un archivo binario
bool gol_load(struct gol *gol, const char *file); bool gol_save(const struct gol *gol, const char *file);
|
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
| |
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.
|
11.2 | Objetos II: Implementar un nuevo objeto `gol_finite`
| |
11.3 | Objetos II: Privatizar la estructura gol y algunos métodos
| |
11.4 | Objetos II: ... | |
... | ... | ... |