@@ -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[]) {
7673Cualquiera de estas funciones pueden fallar, y si lo hacen, nos imposibilitaría a continuar la ejecución.
7774Por esta razon, se chequean errores y se liberan los recursos de forma ordenada. La contra es que
7875el 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