Habrá notado que el estilo de este libro es diferente de muchos de los estilos C tradicionales. Por supuesto, todo el mundo piensa que su propio estilo es el más racional. Sin embargo, el estilo que se emplea aquí tiene detrás una lógica sencilla, que se presentará mezclada con algunas ideas sobre por qué surgieron algunos de los otros estilos.
El estilo está motivado por una cosa: la presentación, tanto impresa como en un seminario. Quizá sus necesidades sean diferentes porque no realiza muchas presentaciones. Sin embargo, el código real se lee muchas más veces de las que se escribe, y por eso debería ser fácil de leer. Mis dos criterios más importantes son la «escaneabilidad» (que se refiere a la facilidad con la que el lector puede comprender el significado de una única línea) y el número de líneas que caben en una página. Lo segundo puede parecer extraño, pero cuando uno da una charla, distrae mucho a la audiencia que el ponente tenga que avanzar y retroceder diapositivas, y sólo unas pocas líneas de más pueden provocar este efecto.
Todo el mundo parece estar de acuerdo en que el código que se pone dentro de llaves debe estar indentado. En lo que la gente no está de acuerdo - y es el sitio donde más incoherencia tienen los estilos - es esto: ¿dónde debe ir la llave de apertura? Esta única cuestión, creo yo, es la que causa tantas variaciones en los estilos de codificación (si quiere ver una enumeración de estilos de codificación, consulte C++ Programming Guidelines, de Tom Plum y Dan Saks, Plum Hall 1991). Intentaré convencerle de que muchos de los estilos de codificación actuales provienen de las restricciones previas al C Estándar (antes de los prototipos de función) y por eso ya no son apropiados.
Lo primero, mi respuesta a esa pregunta clave: la llave de apertura debería ir siempre en la misma línea que el «precursor» (es decir, «cualquier cosa de la que sea cuerpo: una clase, función, definición de objeto, sentencia if, etc.»). Es una regla única y coherente que aplico a todo el código que escribo, y hace que el formateo de código sea mucho más sencillo. Hace más sencilla la «escaneabilidad» - cuando se lee esta línea:
int func(int a);
se sabe, por el punto y coma al final de la línea, que esto es una declaración y no hay nada más, pero al leer la línea:
int func(int a) {
inmediatamente se sabe que se trata de una definición porque la línea termina con una llave de apertura, y no con un punto y coma. Con este enfoque, no hay diferencia a la hora de colocar el paréntesis de apertura en una definición de múltiples líneas:
int func(int a) { int b = a + 1; return b * 2; }
y en una definición de una sola línea, que a menudo se usa para funciones inline:
int func(int a) { return (a + 1) * 2; }
Igualmente, para una clase:
class Thing;
es una declaración del nombre de una clase, y
class Thing {
es una definición de clase. En todos los casos, se puede saber mirando una sola línea si se trata de una declaración o una definición. Y por supuesto, escribir la llave de apertura en la misma línea, en lugar de una línea propia, permite ahorrar espacio en la página.
Así que, ¿por qué hay tantos otros estilos? En concreto, verá que la mayoría de la gente crea clases siguiendo el estilo anterior (que Stroustrup usa en todas las ediciones de su libro The C++ Programming Language de Addison-Wesley) pero crea definiciones de funciones poniendo la llave de apertura en una línea aparte (lo que da lugar a muchos estilos de indentación diferentes). Stroustrup lo hace excepto para funciones inline cortas. Con el enfoque que yo describo aquí, todo es coherente - se nombra lo que sea (class, función, enum, etc.) y en la misma línea se pone la llave de apertura para indicar que el cuerpo de esa cosa viene a continuación. Y también, la llave de apertura se pone en el mismo sitio para funciones inline cortas que para definiciones de funciones ordinarias.
Creo que el estilo de definición de funciones que utiliza mucha gente viene del C anterior a los prototipos de función, en el que no se declaraban los argumentos entre los paréntesis, sino entre el paréntesis de cierre y la llave de apertura (esto demuestra que las raíces de C son el lenguaje ensamblador):
void bar() int x; float y; { /* body here */ }
Aquí, quedaría bastante mal poner la llave de apertura en la misma línea, así que nadie lo hacía. Sin embargo, había distintas opiniones sobre si las llaves debían indentarse con el cuerpo del código o debían dejarse al nivel del «precursor». Así surgieron muchos estilos diferentes.
Hay otros argumentos para poner la llave en la línea siguiente a la declaración (de una clase, struct, función, etc.). Lo siguiente proviene de un lector, y lo presento aquí para que conozca los argumentos:
Los usuarios experimentados de vi (vim) saben que pulsar la tecla «]» dos veces lleva el cursor a la siguiente ocurrencia de «{» (o ^L) en la columna 0. Esta característica es extremadamente útil para moverse por el código (saltando a la siguiente definición de función o clase). [Mi comentario: cuando empecé a trabajar en Unix, GNU Emacs acababa de aparecer y yo me enganché a él. Como resultado, nunca le he encontrado sentido a vi, y por eso yo no pienso en términos de «posiciones en la columna 0». Sin embargo, hay una buena cantidad de usuarios de vi ahí fuera, a los que les afecta esta cuestión.]
Poniendo la «{» en la siguiente línea se eliminan algunas confusiones en sentencias condicionales complejas, lo que favorece la escaneabilidad. Por ejemplo:
if (cond1 && cond2 && cond3) { statement; }
Lo anterior [afirma el lector] tiene mala escaneabilidad. Sin embargo,
if (cond1 && cond2 && cond3) { statement; }
separa el if del cuerpo, lo que mejora la legibilidad. [Su opinión sobre si eso es cierto variará dependiendo de a qué esté acostumbrado.]
Finalmente, es mucho más fácil emparejar visualmente las llaves si están alineadas en la misma columna. Visualmente destacan mucho más. [Fin del comentario del lector]
El tema de dónde poner la llave de apertura es probablemente el asunto en el que hay menos acuerdo. He aprendido a leer ambas formas, y al final cada uno utiliza la que le resulta más cómoda. Sin embargo, he visto que el estándar oficial de codificación de Java (que se puede encontrar en la página de Java de Sun) es prácticamente el mismo que yo he presentado aquí - dado que más personas están empezando a programar en ambos lenguajes, la coherencia entre estilos puede ser útil.
Mi enfoque elimina todas las excepciones y casos especiales, y lógicamente produce también un único estilo de indentación. Incluso dentro del cuerpo de una función, la coherencia se mantiene, como en:
for(int i = 0; i < 100; i++) { cout << i << endl; cout << x * i << endl; }
El estilo es fácil de enseñar y recordar - se usa una regla única y coherente para todo el formato, no una para clases, dos para funciones (funciones inline de una línea vs. multi-línea), y posiblemente otras para bucles for, sentencias if, etc. Creo que sólo por su coherencia ya merece ser tenido en cuenta. Sobre todo, C++ es un lenguaje más nuevo que C, y aunque hay que hacer muchas concesiones a C, no se deberían arrastrar demasiados vestigios que causen problemas en el futuro. Problemas pequeños multiplicados por muchas líneas de código se convierten en grandes problemas. Para un examen minucioso del asunto, aunque trata de C, vea C Style: Standards and Guidelines, de David Straker (Prentice-Hall 1992).
La otra restricción bajo la que debo trabajar es la longitud de la línea, dado que el libro tiene una limitación de 50 caracteres. ¿Qué ocurre si algo es demasiado largo para caber en una línea? Bien, otra vez me esfuerzo en tener una política coherente para las líneas partidas, de modo que sean fácilmente visibles. Siempre que sean parte de una única definición, lista de argumentos, etc., las líneas de continuación deberían indentarse un nivel respecto al comienzo de la definición, lista de argumentos, etc.