Skip to content

Commit da02eb6

Browse files
committed
eze se la come, y qué
1 parent 8924dfb commit da02eb6

1 file changed

Lines changed: 95 additions & 7 deletions

File tree

blog/_posts/2021/2021-05-05-ManejoDeErroresEnC.md

Lines changed: 95 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,15 @@ int main(int argc, const char *argv[]) {
4747
// Como no alloqué nada antes de esta función, no hago nada
4848
// (Se asume que lo que alloque la función socket_init fue liberado si
4949
// tuvo alguna falla interna)
50-
fprintf(stderr, "Falló socket_init (%s)\n", strerror(errno));
5150
return -1;
5251
}
5352
54-
if ( (file = fopen(argv[ARGV_FILEPATH_INDEX], "r")) == NULL) {
55-
fprintf(stderr, "No pude abrir el archivo (%s)\n", strerror(errno));
53+
if ((file = fopen(argv[ARGV_FILEPATH_INDEX], "r")) == NULL) {
5654
socket_uninit(&skt);
5755
return -1;
5856
}
5957
6058
if (socket_connect(&skt, argv[ARGV_HOSTNAME_INDEX], argv[ARGV_SERVICE_INDEX]) < 0) {
61-
fprintf(stderr, "No pude conectarme (%s)\n", strerror(errno));
6259
fclose(file);
6360
socket_uninit(&skt);
6461
return -1;
@@ -76,6 +73,97 @@ int main(int argc, const char *argv[]) {
7673
Cualquiera de estas funciones pueden fallar, y si lo hacen, nos imposibilitaría a continuar la ejecución.
7774
Por esta razon, se chequean errores y se liberan los recursos de forma ordenada. La contra es que
7875
el código se hace mas ilegible. En clases posteriores vamos a ver cómo en C++ podemos tener la misma robustez
79-
que este programa, conservando la legibilidad del primer programa. Mientras tanto, en C nos tenemos que conformar
80-
con esto (en C existen otros métodos para mejorar la legibilidad, pero requieren el uso del infame `goto` y
81-
en la facultad eso es palabra prohibida).
76+
que este programa, conservando la legibilidad del primer programa. Mientras tanto, en C podemos hacer algunas
77+
mejoras. La primera es utilizar funciones auxiliares, para ir manejando de a 1 los recursos allocados en
78+
cada capa.
79+
80+
```c
81+
#define ARGV_HOSTNAME_INDEX 1
82+
#define ARGV_SERVICE_INDEX 2
83+
#define ARGV_FILEPATH_INDEX 3
84+
#define ARGC_MANDATORY_QUANTITY 4
85+
86+
static int _open_file_and_do_something(socket_t *skt, const char *filepath) {
87+
FILE *file;
88+
if ((file = fopen(filepath, "r")) == NULL) {
89+
return -1;
90+
}
91+
92+
int ret = do_something(skt, file);
93+
fclose(file);
94+
return ret;
95+
}
96+
97+
int main(int argc, const char *argv[]) {
98+
if (argc != ARGC_MANDATORY_QUANTITY) {
99+
fprintf(stderr, "Uso: %s <hostname> <servicio> <path al archivo>\n", argv[0]);
100+
return -1;
101+
}
102+
103+
socket_t skt;
104+
105+
if (socket_init(&skt) < 0) {
106+
// Como no alloqué nada antes de esta función, no hago nada
107+
// (Se asume que lo que alloque la función socket_init fue liberado si
108+
// tuvo alguna falla interna)
109+
return -1;
110+
}
111+
112+
if (socket_connect(&skt, argv[ARGV_HOSTNAME_INDEX], argv[ARGV_SERVICE_INDEX]) < 0) {
113+
socket_uninit(&skt);
114+
return -1;
115+
}
116+
117+
int ret = _open_file_and_do_something(&skt, argv[ARGV_FILEPATH_INDEX]);
118+
socket_uninit(&skt);
119+
return ret;
120+
}
121+
```
122+
123+
De esta forma manejaríamos de a 1 recurso por función. La desventaja es que estarías llamando a varias funciones
124+
anidadas (se podría solucionar con funciones `inline`). Otra alternativa es el uso del infame `goto`
125+
126+
127+
```c
128+
#define ARGV_HOSTNAME_INDEX 1
129+
#define ARGV_SERVICE_INDEX 2
130+
#define ARGV_FILEPATH_INDEX 3
131+
#define ARGC_MANDATORY_QUANTITY 4
132+
133+
int main(int argc, const char *argv[]) {
134+
if (argc != ARGC_MANDATORY_QUANTITY) {
135+
fprintf(stderr, "Uso: %s <hostname> <servicio> <path al archivo>\n", argv[0]);
136+
return -1;
137+
}
138+
139+
socket_t skt;
140+
FILE *file;
141+
int ret = 0;
142+
if ((ret = socket_init(&skt)) < 0) {
143+
goto socket_init_failed;
144+
}
145+
146+
if ((file = fopen(argv[ARGV_FILEPATH_INDEX], "r")) == NULL) {
147+
ret = -1;
148+
goto file_open_failed;
149+
}
150+
151+
if ((ret = socket_connect(&skt, argv[ARGV_HOSTNAME_INDEX], argv[ARGV_SERVICE_INDEX])) < 0) {
152+
goto socket_connect_failed;
153+
}
154+
155+
ret = do_something(&skt, file);
156+
157+
socket_connect_failed:
158+
fclose(file);
159+
file_open_failed:
160+
socket_uninit(&skt);
161+
socket_init_failed:
162+
return ret;
163+
}
164+
```
165+
166+
Notar como la liberación de recursos se apilan al final de la función y cada goto libera
167+
**solo los recursos que fueron allocados hasta el momento de esa falla**. De esta forma se
168+
logra un comportamiento similar a lo que sucede con los objetos en C++. Sin embargo,
169+
el uso de `goto` en esta materia (y en esta facultad), está prohibido.

0 commit comments

Comments
 (0)