Habrá notado que el estilo de este libro es diferente a la mayoría de los estilos C tradicionales. Por supuesto, cualquiera puede pensar que su propio estilo es más racional. Sin embargo, el estilo que se emplea aquí tiene una lógica más simple, que se presentará mezclada con las de otros estilos desarrollados.
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 sonar gracioso, 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 puede 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 inconsistencia tienen los estilos - es: ¿Dónde debe ir la llave de apertura? Esta única cuestión, creo yo, es la que causa la mayoría de las variaciones en los estilos de codificación (Si quiere ver una enumeración de estilos de codificación vea C++ Programming Guidelines, de [FIXME:autores] Tom Plum y Dan Saks, Plum Hall 1991), Intentaré convencerle de que muchos de los estilos de codificación actuales provienen de la restricciones previas al C Estándar (antes de los prototipos de función) de manera que no son apropiadas actualmente.
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
consistente 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 un punto y coma. Usando 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 para una definición de una sola línea que a menudo se usa para inlines:
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é tenemos tantos otros estilos? En concreto, verá
que mucha gente crea clases siguiente el estilo anterior (que
Stroustrup usa en todas las ediciones de su libro The
C++ Programming Language de Addison-Wesley) pero crean
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 consistente -
se nombra lo que sea (class
, functión, enum
, etc)
y en la misma línea se pone la llave de apertura para indicar que
el cuerpo de esa cosa está debajo. Y también, la llave de apertura
se pone en el mismo sitio para funciones inline que para
definiciones de funciones ordinarias.
Creo que el estilo de definición de funciones que utiliza mucha gente viene de el antiguo prototipado de funciones de C, en el que no se declaraban los argumentos entre los paréntesis, si no 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 a nivel con el «precursor». De modo que tenemos 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 sepa a qué se refiere.
Los usuarios experimentado 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 defición de función o clase). [Mi comentario: cuando yo trabajaba en Unix, GNU Emacs acababa de aparecer y yo me convertí en un fan suyo. Como resultado, vi nunca ha tenido sentido para mí, y por eso yo no pienso en términos de «situación de columna 0». Sin embargo, hay una buena cantidad de usuarios de vi ahí fuera, a los que les afecta esta característica.]
Poniendo la «{» en la siguiente línea se eliminan algunas confusiones en sentencias condicionales complejas, ayudando a la escaneabilidad.
if (cond1 && cond2 && cond3) { statement; }
Lo anterior [dice el lector] tiene una escaneabilidad pobre. Sin embargo,
if (cond1 && cond2 && cond3) { statement; }
separa el if
del cuerpo, mejorando la
legibilidad. [Sus opiniones sobre si eso es cierto variarán
dependiendo para qué lo haya usado.]
Finalmente, es mucho más fácil visualizar llaves emparejadas 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 encontar en la página de Java de Sun) efectivamente es el mismo que yo he presentado aquí - dado que más personas están empezando a programar en ambos lenguajes, la consistencia entre estilos puede ser útil.
Mi enfoque elimina todas las excepciones y casos especiales, y lógicamente produce un único estilo de indentación, Incluso con un cuerpo de función, la consistencia 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 - use una regla simple y
consistente para todo sus formatos, no una para clases, dos para
funciones (funciones inline de una línea vs. multi-línea), y
posiblemente otras para bucles, sentencias if
,
etc. La consistencia por si sola merece ser tenida en
cuenta. Sobre todo, C++ es un lenguaje más nuevo que C, y aunque
debemos hacer muchas concesiones a C, no deberíamos acarrear demasiados
FIXME:artifacts que nos 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 consistente 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.