2009-08-12 14 views
10

He sido programador de Java y VB.Net durante aproximadamente 4 años y programador de C# durante aproximadamente 6 meses. También utilicé varios lenguajes dinámicos como Perl, Python, PHP y JavaScript.¿Por qué el uso de preprocesadores es menos común en otros lenguajes distintos de C/C++/ObjC?

Nunca he tenido necesidad de un preprocesador.

Mi pregunta es: ¿por qué ve un uso tan extenso de preprocesadores en C, C++ y Objective-C, pero rara vez (o nunca) lo ve en lenguajes como Java, C# o Scala?

+7

Aprendimos de C. Con C/C++/Objective-C, obtuvieron el compilador de C en ese momento justo encima de él porque era más fácil de esa manera. Los idiomas más recientes se diseñaron mejor desde cero. La mayoría de las personas que conozco con antecedentes de C que se han mudado tienen miedo de los preprocesadores. No es bueno para la velocidad, arruina tu diseño, perjudica la comprensión. Ninguna ventaja, excepto que le permite escribir un código peor: ¿por qué alguien podría copiar eso a propósito? –

+2

Ver http://stackoverflow.com/questions/652788/what-is-the-worst-real-world-macros-pre-processor-abuse-youve-ever-come-across y http: //blogs.msdn. com/oldnewthing/archive/2005/01/06/347666.aspx :) –

+1

Tengo varios programas dsp en C++ donde cada ciclo cuenta (sí, realmente). El preprocesador me ayuda bastante durante la optimización. No lo uso para nada más. Lo amo. No podrías sacarlo de mis manos. ¿Has realizado una gran cantidad de programación dsp en tiempo real en esos lenguajes dinámicos? Recuerda que no importa cuántos tipos de programación hayas hecho, simplemente has arañado la superficie de los tipos de programación que se pueden hacer. – Nosredna

Respuesta

13

No conozco Objective-C, por lo que mi respuesta será sobre contrastar el uso del preprocesador en C y C++.

El preprocesador fue originalmente necesario para C por varias razones. Si no recuerdo mal, originalmente C no tenía constantes, por lo que #define era necesario para evitar los números mágicos. Antes de 1999, C no tenía funciones en línea, por lo que #define nuevamente se usaba para crear macros o "pseudo-funciones" para guardar la sobrecarga de una llamada de función, manteniendo el código estructurado. C tampoco tiene polimorfismo en tiempo de ejecución ni en tiempo de compilación, por lo que se necesitan #ifdefs para la compilación condicional. Los compiladores generalmente no eran lo suficientemente inteligentes como para optimizar el código inalcanzable, por lo que, una vez más, se usaron #ifdefs para insertar el código de depuración o de diagnóstico.

El uso del preprocesador en C++ es un retroceso a C, y en general se desaprueba. Las funciones de idioma, como las constantes, las funciones en línea y las plantillas, pueden utilizarse en la mayoría de las situaciones en las que en C hubiera utilizado el preprocesador.

Los pocos casos donde el uso de un preprocesador en C++ es aceptable o incluso necesario incluyen las protecciones para los archivos de cabecera, para evitar que el mismo encabezado se incluya varias veces, #ifdef __cplusplus para usar el mismo encabezado para ambas C y C++, __FILE__ y __LINE__ para el registro, y algunos otros.

El preprocesador también se utiliza a menudo para definiciones específicas de plataforma, aunque C++ Gotchas de Stephen Dewhurst recomienda tener directorios de inclusión separados para las definiciones específicas de plataforma y usarlos en configuraciones de compilación separadas para cada plataforma.

+2

Esa no es la única vez que el uso del preprocesador en C++ se considera correcto. Otros usos ampliamente considerados (aunque no universalmente) aceptables incluyen la salida de '__FILE__' y' __LINE__' en el registro, la macro Boost FOREACH, el control de la función de compilación (incluido el clásico '#ifdef __cplusplus // extern" C "{// #endif '), definiciones específicas de la plataforma (por ejemplo, donde se necesita' __declspec' en MSVC y '__atributo__' en GCC), definiendo NDEBUG antes de incluir , ... –

+0

Estoy corregido. – Dima

1

¡Los idiomas modernos tienen el preprocesador incluido en el idioma en sí! Para C++, el preprocesador es necesario solo para la gestión de módulos y la inclusión condicional, por ejemplo, que es muy útil.

Creo que era una herramienta separada, porque los compiladores no eran una herramienta como los conocemos hoy. Escuché que muy viejos compiladores de C solían producir los tokens para los archivos, luego hacía el resto de la compilación en etapas separadas. La razón principal por la que puedo pensar es que la memoria y otros recursos eran muy escasos en comparación con lo que tenemos hoy.

0

Puede estar seguro de que el lenguaje moderno está escrito en C o C++, y en esa implementación en sí hay macros. Los necesita para tratar con las diferencias del sistema operativo para una cosa. Los idiomas dinámicos/de nivel superior se ajustan y ocultan muchas de las cosas que en algún lugar de la parte inferior necesitan macros.

Además, las macros se utilizan a veces para la velocidad. En lenguajes dinámicos, la velocidad no es tan crucial.

1

El preprocesador en C y C++ tiene dos funciones diferentes

  • que tiran de los archivos juntos en el proceso de construcción - idiomas como Java et al. tienen sus propios mecanismos como la importación de hacer esto

  • realizar sustituciones de texto - esto sigue siendo necesaria en cierta medida en C, pero C++ puede hacerlo (en su mayor parte), utilizando mejor las plantillas

Así tanto C como C++ lo necesitan para el primero de ellos, pero C++ puede chatarrearlo por el segundo, aunque puede ser útil incluso en C++, ver this question desde el día de hoy.

10

La razón por la que no ve el preprocesador utilizado en Java, C# o Scala es que esos lenguajes obviamente no tienen uno.

Uno de los usos más comunes del preprocesador C es ayudar a proporcionar un código específico para la plataforma. Como C (incluyo C++ y Objective-C aquí) es un lenguaje de bajo nivel que necesita interactuar directamente con el sistema operativo, en el código portátil deben existir necesariamente diferentes secciones del código compiladas para diferentes sistemas operativos. Puede encontrar ejemplos extensos de este tipo de cosas en una base de código madura y altamente portátil como zlib.

Como un simple ejemplo, para cerrar un socket de red hay que hacer algo como esto (en algún nivel, esto sin duda puede ser envuelto en una función, pero tiene que existir en algún lugar):

#ifdef WIN32 
    closesocket(s); 
#else 
    close(s); 
#endif 

reciente idiomas que se ejecutan en máquinas virtuales no necesitan las diferentes secciones de código específicas de la plataforma, y ​​pueden escribirse en una única biblioteca portátil estándar.

El preprocesador también proporciona una forma de definir constantes en C, que son proporcionadas por otras características de lenguaje mejores en idiomas más nuevos.

En El diseño y la evolución de C++, Bjarne Stroustrup afirmó que quería eliminar la dependencia del preprocesador en C++, pero no tuvo éxito.

+4

"La razón por la que no ves el preprocesador ... es que esos idiomas ... no tienen uno". !! ¡excelente! – Cheeso

+1

Un lenguaje real podría examinar la variable WIN32, ver que es definitiva y generar la ruta correcta en línea. Prácticamente no hay uso para los preprocesadores en un lenguaje moderno. –

+0

Irónicamente (si no me equivoco), el antecesor de Bjarne para C++, C con Classes, era esencialmente una macrocapa sobre C. –

3

¡Porque Gosling y Heilsberg entienden los riesgos y la deuda técnica incurridos con el mal uso del preprocesamiento!

4

Porque el diseño y los objetivos de estos idiomas no son los mismos.

C se creó teniendo en cuenta el preprocesador como una poderosa herramienta, se utilizó para implementar elementos básicos (como guardias de inclusión) y los desarrolladores pudieron usarlo para optimizar su código a través de macros u opcionalmente incluir/excluir ciertos bloques de código además de otras cosas. C++ heredó la mayor parte de los idiomas de C, las macros no se utilizan para la velocidad más (porque se introdujo en línea), pero sigue siendo usado para un montón de cosas, ver el mensaje What are preprocessor macros good for?

0

Usted debe mirar un poco más de cerca en Perl. Perl admite source filters, que son básicamente preprocesadores de Perl personalizados escritos en Perl :)

0

Java se diseñó para evitar varias características que hacen que C++ sea difícil de usar.

C# copia (o hereda) la mayor parte de la decisión de diseño de Java.

Los lenguajes de programación de nivel superior evitan este tipo de artefactos de bajo nivel.

1

No estoy de acuerdo con lo que parece ser el consenso cuando esa cpp es innecesaria en los idiomas modernos. Tengo muchos casos en los que tengo 3 versiones ligeramente diferentes del mismo programa, y ​​quiero poder hacer un montón de cambios para cada versión.Con CPP, puedo ponerlos todos en bloques #if #else, y puedo definir el #if en la línea de compilación. en Java, necesito crear algún tipo de global estático e inicializarlo en tiempo de compilación. Nunca lo hice para que funcione correctamente.

+0

@Brian: ¡Eww! ¡No me sorprende que no haya funcionado bien! Una mejor solución en Java sería usar subclases o el patrón de "Estrategia" para separar las diferencias entre las versiones. –

+0

Pero si las diferencias son 10 líneas esparcidas por una clase, ¿o peor varias clases? eso es mucho esfuerzo. –

+0

@Brian: no puedes ganar este argumento. Esto es Java, entonces más clases => Mejor ;-) –

8

Cada idioma necesita un mecanismo para compilación separada. Idealmente, el lenguaje distingue las interfaces de las implementaciones, y un módulo depende solo de las interfaces de los módulos que exporta. (Consulte, por ejemplo, Ada, Clu, Modula, etc.)

C   no tiene construcción de idioma para interfaces o implementaciones. Debido a que es vital que diferentes archivos .c compartan una sola vista de interfaces, la disciplina de programación evolucionó al poner declaraciones (es decir, interfaces) en archivos .h y al compartir esas declaraciones/interfaces usando inclusión textual (#include). En el principio  , #define y #ifdef podrían prescindirse, pero #include no podría.

Hoy en día los diseñadores de lenguajes reconocen que la inclusión textual hay manera de ejecutar un ferrocarril, por lo lenguas tienden a correr las interfaces ya sea a compilados por separado (Ada, Modula, OCaml), interfaces generados-compilador (Haskell), o en los sistemas dinámicos eso garantiza la consistencia de la interfaz (Java, Smalltalk). Con tal mecanismo, no hay necesidad de un preprocesador, y hay muchas razones para no tener uno (piense en análisis de código fuente y depurando).

+1

+1 Su respuesta parece ser la más neutral e informativa aquí. Te aplaudo. –

1

El preprocesamiento es muy, muy común en el mundo de Java. Se utiliza para compensar la falta de instalaciones adecuadas de abstracción integradas en el idioma, lo que de otro modo daría lugar a un código repetitivo copiado y pegado sin fin. La razón por la que mucha gente no se da cuenta es que en el mundo Java se llama "generación de código" en lugar de "preprocesamiento", porque "preprocesador" suena como C desagradable, mientras que "generación de código" suena como una herramienta profesional que aprovecha procesos empresariales maduros. Sin embargo, aún es un preprocesamiento, incluso si tiene que pagar una fortuna por una herramienta patentada incompatible no estándar para hacerlo, en lugar de simplemente utilizar las instalaciones integradas en el idioma.

+0

¡Qué brillante respuesta, realmente me gustaría poder darle más de una! Vote! –

Cuestiones relacionadas