Pobre y triste preprocesador


Como todo lenguaje de programación que se precie, C# dispone de de un preprocesador para permitir ciertas tareas que no serían posibles (o muy difíciles) sin él. Aunque técnicamente hablando no es un preprocesador, ya que dichas directivas se procesan simultáneamente durante el análisis léxico.


Y el título de esta entrada tiene una explicación: comparado con el de otros lenguajes de programación, el preprocesador de C# es un juguete; eso no quiere decir que no se puedan hacer las mismas cosas que con, por ejemplo, C++, sino que hay que hacerlas de forma diferente.


Definir una constante
Mientras que en otros lenguajes podemos definir una constante y asignarle un valor, o sólo definirla, en C# sólo podemos hacer esto último, con lo que se elimina cualquier posibilidad de sustitución de macros, tokenización, creación de funciones o la simple sustitución de un número mágico por su equivalente predefinida. C# permite que muchas de estas tareas se puedan realizar de forma más segura (y más complicada también) aprovechando que se trata de un lenguaje interpretado (no hay nada nuevo bajo el sol, y el uso del p-code se viene utilizando desde tiempos inmemoriales, quien recuerde el clipper sabrá de que estoy hablando. De hecho, a nivel teórico no existe mucha diferencia entre el basic tradicional y los modernos lenguajes .NET. Estamos hablando de un código fuente que se transforma en una serie de símbolos binarios que un motor de ejecución ha de interpretar).


Compilación condicional
Basadas en las constantes anteriormente citadas se permite lo que se conoce como compilación condicional. Dado que con el preprocesador no se puede asignar un valor a una constante, la compilación condicional sólo puede comprobar la existencia de dicha constante.


La compilación condicional puede llegar a ser enormemente útil en ciertas situaciones; desde diferenciar una construcción de depuración o final, hasta la inclusión de según qué codigo en según qué situaciones.


Un ejemplo que englobe lo visto hasta ahora podría ser el siguiente:



#define HOLA
#if HOLA

#elif ADIOS

#else

#endif
#undef HOLA


Nada más sencillo ni más elemental. Primero definimos la constante HOLA, luego si dicha constante está definida entramos dentro de #if HOLA, si no está definida continuamos la cadena, y en el momento en el que entremos en uno de los if, ya no se siguen evaluando más condicionales, por lo que si estuviera definido tanto HOLA como ADIOS, el compilador no entraría en en bloque ADIOS.


Directivas de diagnóstico
Aunque no suelen ser muy utilizadas en el desarrollo normal (personalmente recomiendo encarecidamente su uso junto a la compilación condicional para evitar situaciones un tanto kafkianas), sí que son enormemente útiles en el mundo de la metaprogramación. Disponemos de dos directivas,



#warning Esto es un aviso
#error Esto es un error.


La primera línea nos imprimirá un aviso como si se tratara de un aviso generado por el compilador, y la segunda un error con las mismas características que los errores del compilador.


Directiva de región
Estas son nuevas en los lenguajes .NET, y se utilizan para agrupar lógicamente bloques de código para simplificar la lectura del mismo. Personalmente las utiliza muy a menudo y me resultan muy utiles para agrupar algoritmos, separar secciones y en general presentar un código mucho más legible y bonito.


Directivas de línea
Quizás las menos utilizadas de todas, y al igual que las de diagnóstico sólo suelen utilizarse en la metaprogramación. Su función, que quizá parezca un tanto sin sentido, es la de cambiar u ocultar los números de línea del fichero de texto, para forzar los que nosotros queramos.



#line 100
#line default
#line hidden


La primera directiva cambia el número de línea por 100, la segunda restaura los números de linea y la tercera los oculta (es decir, si se produce un error o un aviso, esta información aparecerá sin número de línea). La directiva central también sirve para restaurar el estado oculto.


Conclusión
Aunque muy pobrecitas, las funciones del pseudo preprocesador de C# se muestran suficientes, pero por un justo aprobado. Lo que más echo en falta es el poder definir constantes. Ya sé que existe su equivalente dentro de una clase, pero no es lo mismo. A veces son necesarios valores globales a toda la aplicación, lo que obliga a meterlas dentro de una clase, y ya sabemos que el .NET no trabaja muy fino con los elementos estáticos. La verdas es que con el diseño del .NET y del C# se han lucido en algunos aspectos, y este es uno de ellos. En fin.

13 comentarios sobre “Pobre y triste preprocesador”

  1. Totalmente de acuerdo Rafael, yo al menos considero mas comodo desarrollar en C# que C++ por el simple hecho que todo es menos complicado, incluso se me hace pesado crear un proyecto en C++.

    Ultimamente he estado probando la version Standar y Express de Visual C++ 2005 y pues ninguna me ha gustado mucho, prefiero C#.

    Un Saludo.

    P.D.:C# es el todopoderoso [:D]

  2. Rafa,

    Es que tú lo q quieres es que te pongan todo como en C++ 🙂

    C# antepone la claridad y seguridad a la posibilidad de inventarse «genialidades» que luego provocan un código oscuro o errores sutiles difíciles de seguir, efecto típico por ejemplo de las macros…

    Y no estoy de acuerdo en que C#sea un lenguaje interpretado…
    Mi definición de lenguaje interpretado es muy sencilla: un lenguaje interpretado es aquel en que se permite ejecutar un programa que contenga errores sintácticos. Eso no ocurre en C#, Java ni en UCSD Pascal (donde se usó por primera vez el p-code).

    Slds – Octavio

  3. Juan, trabajar con C# frente a C++ es toda una gozada por lo mismo que comentas, pero a veces se te queda pequeño.

    Octavio: qué bien me conoces en lo del C++, je je. Respecto a lo de interpretado y todo eso, lo cierto es que salvo el análisis sintáctico hay pocas diferencias. Recuerda que había «compiladores» de basic que pasaban el código fuente a p-code… Lo que pasa es que ahora se juega con máquinas virtuales, optimizadores y todo eso, pero al final del todo es lo mismo: código interpretado. Por supuesto, es una opinión, porque si bajamos al nivel más básico de códigos de operación dentro de un microprocesador, también es código interpretado, debajo del byte hexadecimal hay un microcódigo que es realmente el que interpreta esa serie de bits y las ejecuta.

    😉

  4. Yo pienso que cuando se diseño C#, en lo que nos concierne, ese tipo de cuestiones para el preprocesador se contemplarian detenidamente, no sería decisiones tomadas a la ligera. Para esa funcionalidad ya tenemos C++, entonces para que incluirla en un nuevo lenguaje en el que se busca mayor simplicidad y seguridad?

    Si usamos C# y necesitamos este tipo de cosas, tal vez interese programar parte del proyecto o algún módulo del mismo en C++, no??

  5. El codigo .NET es interpretado. Es compilado, se compila a un lenguaje intermedio, y este, la primera vez que se ejecuta se compila a codigo nativo. Las siguientes veces se ejecuta el codigo nativo.

    Un lenguaje interpretado es aquel que se traduce cada vez que se ejecuta al codigo nativo de la maquina que lo ejecuta. Existe bastante diferencia entre interpretado, nativo e intermedio.

  6. Miguel, técnicamente hablando y siendo rigurosos NO es un lenguaje interpretado y estás en toda tu razón, pero viéndolo desde una perspectiva lógica y no formal, ocurre lo que tu dices hasta que dicho bloque de código se va de la caché de compilados y se vuelve a realizar el proceso.

    Un código compilado es aquel que se distribuye de forma binaria según el juego de instrucciones del compilador. El Sistema Operativo lo carga en memoria y salta a la dirección de ejecución y entonces es el propio programa (el código almacenado dentro de él el que se ejecuta). Un programa .NET sufre varias transformaciones y pasos intermedios hasta que lo que quiera que esté almacenado en el ensamblado termina ejecutándose, es decir, lo que quiera que haya en el ensamblado se INTERPRETA (o TRADUCE si quieres) a lo que realmente se va a ejecutar. Semánticamente hablando (en la interpetación 1 del DRAE y casi parte de la 2) es lo mismo que interpretar (pero con más pasos y mejores interpretaciones), frente a directamente ejecutar.

    Para simplificar, mientras que un código compilado tan sólo requiere la carga, distribución en memoria y ejecución, un código no compilado (interpretado) sufre los tres pasos anteriores más al menos uno más de interpretación (traducción) de lo que quiera que haya sido cargado a memoria a lo que realmente se va a ejecutar.

  7. soy estudiante de programacion y necesito hacer una funcion que me diga el numero de lineas que contiene un fichero que yo abro
    si algiien sabe c´´omo hacerlo, o orientarme, le estaria muy agradecido.
    Saludos

  8. Este no es el mejor lugar para responderte, pero debes abrir el fichero en modo texto, leerlo línea a línea hasta el final e ir contándolas. Y ya está. Depende del lenguaje y el sistema operativo se hace de una forma o de otra.

    En C#, y a lo bruto, sería algo así:

    string[] lineas=System.File.ReadAllLines();

    en lineas.Length tienes el número de líneas. En otros lenguajes es algo más trabajoso.

  9. Si bien me ha ayudado mucho, creo que sería mucho más feliz si C++ no tuviera preprocesador. Como dijo Dan, es de lo peor del lenguaje (si es que se lo puede considerar parte de él). Como será que Stroustrup en su libro ‘The Design and Evolution of C++’ fija bien su postura para que a futuro el preprocesador desaparezca. De hecho, ahora el lenguage tiene muchas caracteristicas para que el mismo no tenga la utilidad de antes (constantes, templates, funciones inline, etc).

    Salu2

  10. No sólo a ti te gusta… sino héchale una mirada a la bibliteca de metaprogramacion mediante preprocesador de boost. La verdad que esta muy buena 😉
    http://www.boost.org/libs/preprocessor/doc/index.html

    Lo que no me gusta es que es un arma de doble filo. Depurar un error en algo hecho mediante el preprocesador es dificultoso. De todas maneras, mas de una vez me ‘salvado’ con alguna solución ‘mágica’. Actualmente lo más valioso para mi es la posibilidad de generar compilación condicional.

    Salu2

Responder a anonymous Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *