¿La generación del código fuente es un antipatrón?


118

Si se puede generar algo, entonces eso es información, no código.

Dado eso, ¿no es toda esta idea de generación de código fuente un malentendido? Es decir, si hay un generador de código para algo, entonces ¿por qué no hacer de ese algo una función adecuada que pueda recibir los parámetros requeridos y realizar la acción correcta que el código "generado" habría hecho?

Si se hace por razones de rendimiento, eso suena como una deficiencia del compilador.

Si se está haciendo para unir dos idiomas, entonces eso suena como una falta de biblioteca de interfaz.

¿Me estoy perdiendo de algo?

Sé que el código también es información. Lo que no entiendo es, ¿por qué generar código fuente ? ¿Por qué no convertirlo en una función que pueda aceptar parámetros y actuar sobre ellos?


11
Un término asociado con la generación de código es metaprogramación
UselesssCat

44
en.wikipedia.org/wiki/Code_as_data , Lisp, FP, scripting, metaprogramming, Von Neumann / modificó la arquitectura de Harvard, etc. Se ha cubierto hasta la saciedad . tl; dr la distinción "código fuente" frente a "código de salida", "código" frente a "datos", etc. están destinados a simplificar las cosas. Nunca deberían ser dogmáticos .
vaxquis

99
@Utku, las mejores razones para generar código a menudo se relacionan con querer proporcionar una descripción de nivel superior a la que puede expresar su idioma actual . Si el compilador puede o no puede crear un código eficiente realmente no tiene nada que ver con eso. Considere los generadores de analizadores: un lexer generado por flexo un analizador generado por bisonseguramente será más predecible, más correcto y, a menudo, más rápido de ejecutar que los equivalentes escritos a mano en C; y construido a partir de mucho menos código (por lo tanto, también es menos trabajo de mantenimiento).
Charles Duffy

1
Tal vez usted proviene de un idioma que no tiene muchos elementos funcionales, pero en muchos idiomas las funciones son de primera clase, puede pasarlas, por lo que en ese tipo de idiomas el código es información, y puede tratarlo así.
Restioson

1
@Restioson en un código de lenguaje funcional no son datos. Las funciones de primera clase significan exactamente eso: las funciones son datos. Y no necesariamente datos particularmente buenos: no necesariamente puede mutarlos solo un poco (como mutar todas las adiciones dentro de las funciones en sustracciones, por ejemplo). El código es información en lenguajes homoicónicos. (la mayoría de los lenguajes homoicónicos tienen funciones de primera clase. Pero lo contrario no es cierto).
Lyndon White

Respuestas:


150

¿La generación del código fuente es un anti patrón?

Técnicamente, si generamos código, no es fuente, incluso si es texto que los humanos puedan leer. El código fuente es un código original, generado por un ser humano u otra inteligencia verdadera, no traducido mecánicamente y no reproducible inmediatamente desde la fuente (verdadera) (directa o indirectamente).

Si se puede generar algo, entonces esa cosa son datos, no código.

Yo diría que todo son datos de todos modos. Incluso el código fuente. Especialmente el código fuente! El código fuente son solo datos en un lenguaje diseñado para realizar tareas de programación. Estos datos deben traducirse, interpretarse, compilarse, generarse según sea necesario en otras formas, de datos, algunos de los cuales pueden ser ejecutables.

El procesador ejecuta instrucciones sin memoria. La misma memoria que se usa para los datos. Antes de que el procesador ejecute instrucciones, el programa se carga en la memoria como datos .

Entonces, todo son datos , incluso el código .

Dado que [el código generado es información], ¿no es toda esta idea de generación de código un malentendido?

Está perfectamente bien tener múltiples pasos en la compilación, uno de los cuales puede ser la generación de código intermedio como texto.

Es decir, si hay un generador de código para algo, entonces ¿por qué no hacer de ese algo una función adecuada que pueda recibir los parámetros requeridos y realizar la acción correcta que el código "generado" habría hecho?

Esa es una forma, pero hay otras.


La salida de la generación de código es texto, que es algo diseñado para ser utilizado por un humano.

No todos los formularios de texto están destinados al consumo humano. En particular, el código generado (como texto) generalmente está destinado al consumo del compilador, no al consumo humano.


El código fuente se considera el original: el maestro: lo que editamos y desarrollamos; lo que archivamos usando el control del código fuente. El código generado, incluso cuando el texto es legible para humanos, generalmente se regenera a partir del código fuente original . El código generado, en general, no tiene que estar bajo el control de la fuente, ya que se regenera durante la compilación.


1
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
maple_shaft

65

Razonamiento práctico

OK, sé que el código también es información. Lo que no entiendo es, ¿por qué generar código fuente?

A partir de esta edición, supongo que está preguntando en un nivel bastante práctico, no en informática teórica.

La razón clásica para generar código fuente en lenguajes estáticos como Java es que los lenguajes así simplemente no vienen con herramientas fáciles de usar en el lenguaje para hacer cosas muy dinámicas. Por ejemplo, en los días formativos de Java, simplemente no era posible crear fácilmente una clase con un nombre dinámico (que coincida con un nombre de tabla de una base de datos) y métodos dinámicos (atributos coincidentes de esa tabla) con tipos de datos dinámicos (coincidencia los tipos de dichos atributos). Especialmente porque Java le da mucha importancia, es decir, garantías, a poder detectar errores de tipo en tiempo de compilación.

Entonces, en tal configuración, un programador solo puede crear código Java y escribir muchas líneas de código manualmente. A menudo, el programador encontrará que cada vez que cambia una tabla, tiene que regresar y cambiar el código para que coincida; y si olvida eso, suceden cosas malas. Por lo tanto, el programador llegará al punto en el que escribe algunas herramientas que lo hacen por él. Y, por lo tanto, el camino comienza a generar códigos cada vez más inteligentes.

(Sí, podría generar el código de bytes sobre la marcha, pero programar tal cosa en Java no sería algo que un programador aleatorio haría solo entre escribir unas pocas líneas de código de dominio).

Compare esto con los lenguajes que son muy dinámicos, por ejemplo, Ruby, que consideraría la antítesis de Java en la mayoría de los aspectos (tenga en cuenta que lo digo sin valorar ninguno de los dos enfoques; simplemente son diferentes). Aquí es 100% normal y estándar generar dinámicamente clases, métodos, etc. en tiempo de ejecución, y lo más importante, el programador puede hacerlo trivialmente en el código, sin pasar a un nivel "meta". Sí, cosas como Ruby on Rails vienen con la generación de código, pero descubrimos en nuestro trabajo que básicamente lo usamos como una especie de "modo tutorial" avanzado para nuevos programadores, pero después de un tiempo se vuelve superfluo (ya que hay muy poco código escribir en ese ecosistema que cuando sabes lo que estás haciendo, escribirlo manualmente se vuelve más rápido que limpiar el código generado).

Estos son solo dos ejemplos prácticos del "mundo real". Entonces tienes lenguajes como LISP donde el código es datos, literalmente. Por otro lado, en los lenguajes compilados (sin un motor de tiempo de ejecución como Java o Ruby), existe (o no he estado al día con las características modernas de C ++ ...) simplemente no hay concepto de definir nombres de clases o métodos en tiempo de ejecución, por lo que la generación de código, el proceso de compilación es la herramienta elegida para la mayoría de las cosas (otros ejemplos más específicos de C / C ++ serían cosas como flex, yacc, etc.).


1
Creo que esto es mejor que las respuestas más votadas. En particular, el ejemplo mencionado con Java y la programación de bases de datos hace un trabajo mucho mejor al abordar realmente por qué se usa la generación de código y es una herramienta válida.
Panzercrisis

En estos días, ¿es posible en Java crear tablas dinámicas a partir de una base de datos? ¿O solo usando un ORM?
Noumenon

"(o era, no me he mantenido al día con las características modernas de C ++ ...)" ¿seguramente esto ha sido posible en C ++ durante más de dos décadas gracias a los punteros de función? No lo he probado, pero estoy seguro de que posiblemente debería asignar una matriz de caracteres, llenarla con código de máquina y luego lanzar un puntero al primer elemento a un puntero de función y luego ejecutarlo. (Suponiendo que la plataforma de destino no tenga alguna medida de seguridad que lo detenga, lo que bien podría hacer).
Pharap,

1
"asignar un conjunto de caracteres, llenarlo con código de máquina y luego lanzar un puntero al primer elemento a un puntero de función y luego ejecutarlo?" Además de ser un comportamiento indefinido, es el equivalente en C ++ de "generar el código de bytes sobre la marcha". Cae en la misma categoría de "no considerado por los programadores ordinarios"
Caleth

1
@Pharap, "seguramente esto ha sido posible en C ++ por más de dos décadas" ... Tuve que reír un poco; Hace aproximadamente dos décadas desde la última vez que codifiqué C ++. :) Pero mi frase sobre C ++ se formuló mal de todos modos. Lo he cambiado un poco, ahora debería estar más claro lo que quise decir.
AnoE

44

¿Por qué generar código?

Porque programar con tarjetas perforadas (o códigos alt en el bloc de notas ) es una molestia.

Si se hace por razones de rendimiento, eso suena como una deficiencia del compilador.

Cierto. No me importa el rendimiento a menos que me vean obligado a hacerlo.

Si se está haciendo para unir dos idiomas, entonces eso suena como una falta de biblioteca de interfaz.

Hmm, no tengo idea de lo que estás hablando.

Mire, es así: el código fuente generado y retenido es siempre y para siempre un dolor de cabeza. Existe por una sola razón. Alguien quiere trabajar en un idioma mientras alguien más insiste en trabajar en otro y ninguno de los dos puede molestarse en descubrir cómo interactuar entre ellos para que uno de ellos descubra cómo convertir su idioma favorito en el idioma impuesto para que puedan hacer lo que quieran. ellos quieren.

Lo cual está bien hasta que tenga que mantenerlo. En ese momento todos ustedes pueden ir a morir.

¿Es un anti patrón? Suspiro, no. Muchos idiomas ni siquiera existirían si no estuviéramos dispuestos a despedirnos de las deficiencias de los idiomas anteriores y generar el código de los idiomas más antiguos es la cantidad de nuevos idiomas que comienzan.

Es una base de código que se deja en un mosaico de monstruos de Frankenstein medio convertido que no puedo soportar. El código generado es un código intocable. Odio mirar el código intocable. Sin embargo, la gente sigue revisándolo. ¿POR QUÉ? También podrías estar registrando el ejecutable.

Bueno, ahora estoy despotricando. Mi punto es que todos estamos "generando código". Es cuando tratas el código generado como el código fuente que me estás volviendo loco. Solo porque parece que el código fuente no lo convierte en código fuente.


41
Si lo genera, no es el código de FUENTE. Es un código intermedio. Voy a llorar ahora.
candied_orange

65
ARG !!! ¡No importa cómo se vea! Texto, binario, ADN, si no es la FUENTE, no es lo que debe tocar al hacer cambios. No es asunto de nadie si mi proceso de compilación tiene 42 idiomas intermedios por los que pasa. Deja de tocarlos. Deje de registrarlos. Realice sus cambios en la fuente.
candied_orange

24
XML es texto y claramente no está destinado al consumo humano. :-)
Nick Keighley

38
@utku: "Si algo no está destinado a ser consumido por un humano, no debería ser texto": estoy completamente en desacuerdo. Algunos contraejemplos de la parte superior de mi cabeza: el protocolo HTTP, las codificaciones MIME, los archivos PEM, prácticamente cualquier cosa que use base64 en cualquier lugar. Hay muchas razones para codificar datos en una transmisión segura de 7 bits, incluso si ningún humano debería verlos. Sin mencionar el espacio mucho más grande de cosas con las que normalmente un humano nunca debería interactuar, pero que pueden querer ocasionalmente: archivos de registro, /etc/archivos en Unix, etc.
Daniel Pryden

12
No creo que "programar con tarjetas perforadas" signifique lo que crees que significa. He estado allí, he hecho eso, y sí, fue un dolor; pero no tiene conexión con el "código generado". Una baraja de tarjetas perforadas es solo otro tipo de archivo, como un archivo en disco, un archivo en cinta o un archivo en una tarjeta SD. En el pasado, escribíamos datos en mazos de cartas y leíamos datos de ellos. Entonces, si la razón por la que generamos código es porque la programación con tarjetas perforadas es una molestia, entonces eso implica que la programación con cualquier tipo de almacenamiento de datos es una molestia.
Solomon Slow

41

por qué generar código fuente

El caso de uso más frecuente para los generadores de código con los que tuve que trabajar en mi carrera fueron los generadores que

  • tomó una metadescripción de alto nivel para algún tipo de modelo de datos o esquema de base de datos como entrada (tal vez un esquema relacional o algún tipo de esquema XML)

  • y produjo código CRUD de placa de caldera para clases de acceso a datos como salida, y tal vez cosas adicionales como los correspondientes SQL o documentación.

El beneficio aquí es que de una línea de una especificación de entrada corta obtienes de 5 a 10 líneas de código depurable, seguro de tipo, libre de errores (se supone que la salida de los generadores de código está madura) que de lo contrario tuvo que implementar y mantener manualmente. Puede imaginar cuánto reduce esto el mantenimiento y el esfuerzo de evolución.

Déjame responder también a tu pregunta inicial

¿La generación del código fuente es un anti patrón?

No, no la generación del código fuente per se, pero de hecho hay algunas trampas. Como se indica en The Pragmatic Programmer , uno debe evitar el uso de un generador de código cuando produce código que es difícil de entender . De lo contrario, los mayores esfuerzos para usar o depurar este código pueden superar fácilmente el esfuerzo ahorrado al no escribir el código manualmente.

También me gustaría agregar que la mayoría de las veces es una buena idea separar físicamente las partes generadas del código del código escrito manualmente de una manera que la regeneración no sobrescriba ningún cambio manual. Sin embargo, también me he ocupado de la situación más de una vez en la que la tarea consistía en migrar un código escrito en el lenguaje antiguo X a otro lenguaje más moderno Y, con la intención de realizar el mantenimiento posterior en el lenguaje Y. Este es un uso válido caso para la generación de código de una sola vez.


Estoy de acuerdo con esta respuesta Usando algo como Torque para Java, puedo hacer la generación automática de archivos fuente Java, con campos que coinciden con la base de datos SQL. Esto hace que las operaciones crud sean mucho más fáciles. El principal beneficio es la seguridad de tipo, que incluye solo poder hacer referencia a los campos que existen en la base de datos (Gracias autocompletar).
MTilsted

Sí, para los idiomas escritos estáticamente, esta es la parte importante: puede asegurarse de que su código escrito a mano realmente se ajuste al generado.
Paŭlo Ebermann

"migrar un código escrito en un lenguaje antiguo" - incluso entonces, la generación de código de una sola vez puede ser un gran dolor. Por ejemplo, después de algunos cambios manuales, detecta un error en el generador y necesita rehacer la generación después de la corrección. Afortunadamente, git o similar generalmente puede aliviar el dolor.
maaartinus

13

¿Por qué generar código fuente?

He encontrado dos casos de uso para el código generado (en el momento de la compilación, y nunca registrado):

  1. Genere automáticamente código repetitivo como getters / setters, toString, equals y hashCode a partir de un lenguaje creado para especificar tales cosas (por ejemplo, project lombok para Java)
  2. Genere automáticamente clases de tipo DTO a partir de alguna especificación de interfaz (REST, SOAP, lo que sea) para usar en el código principal. Esto es similar a su problema de puente de idioma, pero termina siendo más limpio y simple, con un mejor manejo de tipos que tratando de implementar lo mismo sin clases generadas.

15
Código altamente repetitivo en lenguajes inexpresivos. Por ejemplo, tuve que escribir código que esencial hiciera lo mismo en muchas estructuras de datos similares pero no idénticas. Probablemente podría haber hecho algo así como una plantilla de C ++ (oye, ¿no es esa generación de código?). Pero estaba usando C. La generación de código me salvó escribiendo muchos códigos casi idénticos.
Nick Keighley

1
@NickKeighley ¿Quizás su cadena de herramientas no le permitía usar otro lenguaje más adecuado?
Wilson

77
Por lo general, no puede elegir el idioma de implementación. El proyecto estaba en C, esa no era una opción.
Nick Keighley

1
@Wilson, los lenguajes más expresivos a menudo usan la generación de código (por ejemplo, macros lisp, ruby ​​on rails), mientras tanto no requieren que se guarden como texto.
Pete Kirkham el

44
Sí, la generación de código es esencialmente metaprogramación. Los lenguajes como Ruby le permiten hacer metaprogramación en el lenguaje mismo, pero C no lo hace, por lo que debe usar la generación de código.
Sean Burton el

13

Sussmann tenía mucho más interesante que decir sobre tales cosas en su clásico "Estructura e interpretación de programas de computadora", principalmente sobre la dualidad de datos de código.

Para mí, el uso principal de la generación de código adhoc es hacer uso de un compilador disponible para convertir algún pequeño lenguaje específico de dominio en algo que pueda vincular a mis programas. Piense en BNF, piense en ASN1 (en realidad, no lo haga, es feo), piense en las hojas de cálculo del diccionario de datos.

Los lenguajes específicos de dominio triviales pueden ahorrar mucho tiempo, y generar algo que pueda compilarse con herramientas de lenguaje estándar es el camino a seguir al crear tales cosas, que preferiría editar, un analizador pirateado no trivial en cualquier idioma nativo que sea escritura, o el BNF para uno generado automáticamente?

Al generar texto que luego se envía a algún compilador del sistema, obtengo toda la optimización de compiladores y la configuración específica del sistema sin tener que pensarlo.

Estoy usando efectivamente el lenguaje de entrada del compilador como otra representación intermedia, ¿cuál es el problema? Los archivos de texto no son inherentemente código fuente, pueden ser un IR para un compilador , y si se parecen a C o C ++ o Java o lo que sea, ¿a quién le importa?

Ahora, si tiene dificultades para pensar , puede editar la SALIDA del analizador de idioma de juguetes, lo que claramente decepcionará la próxima vez que alguien edite los archivos de idioma de entrada y se reconstruya, la respuesta es no enviar el IR generado automáticamente al repositorio, téngalo generado por su cadena de herramientas (y evite tener esas personas en su grupo de desarrollo, por lo general son más felices trabajando en marketing).

Esto no es tanto un fracaso de la expresividad en nuestros idiomas, como una expresión del hecho de que a veces puede obtener (o masajear) partes de la especificación en una forma que se puede convertir automáticamente en código, y que generalmente generará mucho menos errores y ser mucho más fácil de mantener. Si puedo darles a nuestros muchachos de prueba y configuración una hoja de cálculo que pueden ajustar y una herramienta que luego ejecutan que toma esos datos y escupe un archivo hexadecimal completo para el flash en mi ECU, entonces es un gran ahorro de tiempo que alguien traduzca manualmente la última configuración en un conjunto de constantes en el idioma del día (Completo con errores tipográficos).

Lo mismo con construir modelos en Simulink y luego generar C con RTW y luego compilar para apuntar con cualquier herramienta que tenga sentido, el C intermedio es ilegible, ¿y qué? El material de alto nivel de Matlab RTW solo necesita conocer un subconjunto de C, y el compilador de C se encarga de los detalles de la plataforma. El único momento en que un ser humano tiene que arrastrarse a través del C generado es cuando los scripts RTW tienen un error, y ese tipo de cosas es mucho más fácil de depurar con un IR legible nominalmente humano que con un árbol de análisis binario.

Por supuesto, puede escribir tales cosas para generar bytecode o incluso código ejecutable, pero ¿por qué haría eso? Tenemos herramientas para convertir un IR a esas cosas.


Esto es bueno, pero agregaría que hay una compensación al determinar qué IR usar: usar C como IR hace que algunas cosas sean más fáciles y otras más difíciles, en comparación con, por ejemplo, el lenguaje ensamblador x86. La elección es aún más significativa cuando se elige entre, por ejemplo, el código de lenguaje Java y el código de bytes de Java, ya que hay muchas más operaciones que solo existen en uno u otro idioma.
Daniel Pryden

2
¡Pero el lenguaje ensamblador X86 genera un IR deficiente cuando apunta a un núcleo ARM o PPC! Todas las cosas son una compensación en ingeniería, por eso lo llaman Ingeniería. Uno esperaría que las posibilidades del código de bytes de Java fueran un superconjunto estricto de las posibilidades del lenguaje Java, y que esto es generalmente cierto a medida que te acercas al metal independientemente de la cadena de herramientas y donde inyectas el IR.
Dan Mills

Oh, estoy totalmente de acuerdo: mi comentario fue en respuesta a su párrafo final preguntando por qué alguna vez emitió bytecode o alguna cosa de nivel inferior, a veces necesita el nivel inferior. (En Java específicamente, hay muchas cosas útiles que puedes hacer con bytecode que no puedes hacer en el lenguaje Java en sí).
Daniel Pryden

2
No estoy en desacuerdo, pero hay un costo por usar un IR más cercano al metal, no solo en generalidades reducidas, sino en el hecho de que generalmente terminas siendo responsable de más de la realmente molesta optimización de bajo nivel. El hecho de que generalmente en estos días pensamos en términos de optimización de la elección del algoritmo en lugar de la implementación es una reflexión sobre cuán lejos han llegado los compiladores, a veces hay que acercarse mucho al metal en estas cosas, pero piense dos veces antes de tirar los compiladores capacidad de optimizar usando un nivel demasiado bajo de un IR.
Dan Mills

1
"suelen ser más felices trabajando en marketing" Catty, pero divertido.
dmckee

13

Respuesta pragmática: ¿la generación de código es necesaria y útil? ¿Proporciona algo que es realmente muy útil y necesario para la base de código patentada, o parece que simplemente crea otra forma de hacer las cosas de una manera que contribuya con una sobrecarga intelectual para obtener resultados subóptimos?

OK, sé que el código también es información. Lo que no entiendo es, ¿por qué generar código? ¿Por qué no convertirlo en una función que pueda aceptar parámetros y actuar sobre ellos?

Si tiene que hacer esta pregunta y no hay una respuesta clara, entonces probablemente la generación de código sea superflua y simplemente contribuya con exotismo y una gran cantidad de sobrecarga intelectual a su base de código.

Mientras tanto, si toma algo como OpenShadingLanguage: https://github.com/imageworks/OpenShadingLanguage

... entonces tales preguntas no necesitan ser planteadas ya que son respondidas de inmediato por los impresionantes resultados.

OSL utiliza el marco del compilador LLVM para traducir redes de sombreadores en código de máquina sobre la marcha (justo a tiempo o "JIT"), y en el proceso optimiza en gran medida los sombreadores y las redes con pleno conocimiento de los parámetros del sombreador y otros valores de tiempo de ejecución que no podrían se conocen cuando los sombreadores se compilaron a partir del código fuente. Como resultado, ¡vemos que nuestras redes de sombreado OSL se ejecutan un 25% más rápido que los sombreadores equivalentes hechos a mano en C! (Así funcionaban nuestros viejos sombreadores en nuestro renderizador).

En tal caso, no necesita cuestionar la existencia del generador de código. Si trabaja en este tipo de dominio de efectos visuales, su respuesta inmediata suele ser más de "¡cállate y toma mi dinero!" o "wow, también necesitamos hacer algo como esto".


traducir redes de sombreadores en código de máquina . Esto suena como un compilador en lugar de un generador de código, ¿no?
Utku

2
Básicamente toma una red nodal que el usuario conecta y genera un código intermediario que es compilado JIT por LLVM. La distinción entre compilador y generador de código es algo confusa. ¿Estaba pensando más en las líneas de características de generación de código en lenguajes como plantillas en C ++ o el preprocesador de C?

Estaba pensando en cualquier generador que produjera código fuente.
Utku

Ya veo, donde la producción sigue siendo para consumo humano, supongo. OpenSL también genera código fuente intermedio, pero es un código de bajo nivel que está cerca del ensamblaje para el consumo de LLVM. Por lo general, no es el código que debe mantenerse (en cambio, los programadores mantienen los nodos utilizados para generar el código). La mayoría de las veces creo que esos tipos de generadores de código son más propensos a ser abusados ​​que lo suficientemente útiles como para justificar su valor, especialmente si tiene que regenerar constantemente el código como parte de su proceso de compilación. A veces todavía tienen un lugar genuino para abordar las deficiencias ...

... del idioma (s) disponible cuando se usa para un dominio particular. QT tiene uno de esos controvertidos con su compilador de metaobjetos (MOC). El MOC reduce la placa repetitiva que normalmente necesitaría para proporcionar propiedades y reflexión y señales y ranuras, etc. en C ++, pero no en tal medida que justifique claramente su existencia. A menudo pienso que QT podría haber sido mejor sin la carga engorrosa de la generación de código del MOC.

8

No, generar código intermedio no es un antipatrón. La respuesta a la otra parte de su pregunta, "¿Por qué hacerlo?", Es una pregunta muy amplia (y separada), aunque de todos modos daré algunas razones.

Ramificaciones históricas de nunca tener código intermedio legible por humanos

Tomemos C y C ++ como ejemplos, ya que se encuentran entre los lenguajes más famosos.

Debe tener en cuenta que la procesión lógica de compilar código C no genera código de máquina, sino código de ensamblaje legible por humanos. Del mismo modo, los viejos compiladores de C ++ solían compilar físicamente el código de C ++ en código C. En esa cadena de eventos, puede compilar desde el código legible por humanos 1 al código legible por humanos 2 al código legible por humanos 3 al código de máquina. "¿Por qué?" Por qué no?

Si nunca se generó un código intermedio legible para humanos, es posible que ni siquiera tengamos C o C ++. Esa es ciertamente una posibilidad; las personas toman el camino de menor resistencia a sus objetivos, y si algún otro lenguaje ganó fuerza primero debido al estancamiento del desarrollo de C, C podría haber muerto mientras aún era joven. Por supuesto, podría argumentar "Pero entonces quizás estaríamos usando otro idioma, y ​​quizás sería mejor". Tal vez, o tal vez sería peor. O tal vez todos todavía estaríamos escribiendo en asamblea.

¿Por qué usar código intermedio legible por humanos?

  1. A veces se desea un código intermedio para que pueda modificarlo antes del siguiente paso en la construcción. Admito que este punto es el más débil.
  2. A veces es porque el trabajo original no se realizó en ningún lenguaje legible por los humanos, sino en una herramienta de modelado GUI.
  3. A veces es necesario hacer algo muy repetitivo, y el lenguaje no debe adaptarse a lo que está haciendo porque es algo tan específico o tan complicado que no tiene por qué aumentar la complejidad o la gramática del lenguaje de programación solo para adaptarse tú.
  4. A veces necesitas hacer algo muy repetitivo, y no hay forma posible de introducir lo que quieres en el idioma de manera genérica; o no puede representarse o entrar en conflicto con la gramática del lenguaje.
  5. Uno de los objetivos de las computadoras es reducir el esfuerzo humano y, a veces, el código que es poco probable que se vuelva a tocar (baja probabilidad de mantenimiento) puede tener un metacódigo escrito para generar su código más largo en una décima parte del tiempo; si puedo hacerlo en 1 día en lugar de 2 semanas y no es probable que se mantenga siempre, entonces mejor generarlo - y ante la posibilidad de que alguien 5 años a partir de ahora es molesto porque en realidad no necesito para mantenerla, a continuación, pueden pasar las 2 semanas escribiéndolo completamente si lo desean, o estar molestos por 1 semana de mantener el código incómodo (pero todavía tenemos 1 semana de anticipación en ese momento), y eso es si ese mantenimiento debe hacerse en absoluto .
  6. Estoy seguro de que hay más razones por las que estoy pasando por alto.

Ejemplo

He trabajado en proyectos antes donde el código necesita ser generado en base a datos o información en algún otro documento. Por ejemplo, un proyecto tenía todos sus mensajes de red y datos constantes definidos en una hoja de cálculo y una herramienta que iría a través de la hoja de cálculo y generaría una gran cantidad de código C ++ y Java que nos permite trabajar con esos mensajes.

No digo que esa sea la mejor manera de configurar ese proyecto (no era parte de su inicio), pero eso fue lo que tuvimos, y fueron cientos (quizás incluso miles, no estoy seguro) de estructuras, objetos y constantes. que se estaban generando; en ese punto, probablemente sea demasiado tarde para intentar rehacerlo en algo como Rhapsody. Pero incluso si se rehizo en algo como Rhapsody, de todos modos todavía tenemos código generado a partir de Rhapsody .

Además, tener todos esos datos en una hoja de cálculo era bueno de una manera: nos permitía representar los datos de una manera que no podríamos tener si solo estuviera en los archivos de código fuente.

Ejemplo 2

Cuando trabajé en la construcción del compilador, utilicé la herramienta Antlr para hacer mi lexing y análisis. Especifiqué una gramática de lenguaje, luego usé la herramienta para escupir una tonelada de código en C ++ o Java, luego usé ese código generado junto con mi propio código y lo incluí en la compilación.

¿De qué otra forma debería haberse hecho eso? Quizás podrías encontrar otra forma; Probablemente hay otras formas. Pero para ese trabajo, las otras formas no habrían sido mejores que el código lex / parse generado que tenía.


He usado código intermedio como una especie de formato de archivo y rastreo de depuración cuando los dos sistemas eran incompatibles pero tenían una API estable de algún tipo, en un lenguaje de script muy esotérico. No estaba destinado a leerse manualmente, pero podría haber sido de la misma manera que podría haber sido xml. Pero esto es más común de lo que piensas después de que todas las páginas web funcionan de esta manera, como alguien señaló.
joojaa

7

Lo que te falta es reutilizar .

Tenemos una herramienta increíble para convertir el texto del código fuente en binario, llamado compilador. Sus entradas están bien definidas (¡por lo general!), Y ha trabajado mucho para refinar cómo funciona la optimización. Si realmente desea utilizar el compilador para llevar a cabo algunas operaciones, desea utilizar un compilador existente y no escribir el suyo propio.

Mucha gente inventa nuevos lenguajes de programación y escribe sus propios compiladores. Casi sin excepción, todos lo hacen porque disfrutan el desafío, no porque necesitan las características que proporciona ese lenguaje. Todo lo que hacen se puede hacer en otro idioma; simplemente están creando un nuevo lenguaje porque les gustan esas características. Sin embargo, lo que no les conseguirá es un compilador bien optimizado, rápido, eficiente y optimizador. Les dará algo que puede convertir el texto en binario, claro, pero no será tan bueno como todos los compiladores existentes .

El texto no es solo algo que los humanos leen y escriben. Las computadoras están perfectamente en casa con texto también. De hecho, formatos como XML (y otros formatos relacionados) son exitosos porque usan texto sin formato. Los formatos de archivos binarios a menudo son oscuros y están poco documentados, y un lector no puede descubrir fácilmente cómo funcionan. XML es relativamente autodocumentado, lo que facilita a las personas escribir código que utiliza archivos con formato XML. Y todos los lenguajes de programación están configurados para leer y escribir archivos de texto.

Entonces, supongamos que desea agregar alguna nueva instalación para facilitarle la vida. Quizás es una herramienta de diseño de GUI. Quizás son las interfaces de señales y ranuras las que proporciona Qt . Tal vez sea la forma en que Code Composer Studio de TI le permite configurar el dispositivo con el que está trabajando y extraer las bibliotecas correctas en la compilación. Quizás esté tomando un diccionario de datos y autodefiniendo typedefs y definiciones de variables globales (sí, esto sigue siendo algo muy importante en el software integrado). Sea lo que sea, la forma más eficiente de aprovechar su compilador existente es crear una herramienta que tome su configuración de lo que sea y produzca automáticamente el código en el idioma que elija.

Es fácil de desarrollar y de probar, porque sabes lo que está pasando y puedes leer el código fuente que escupe. No necesita gastar muchos años en construir un compilador para rivalizar con GCC. No es necesario que aprenda un idioma completamente nuevo ni que otras personas lo hagan. Todo lo que necesita hacer es automatizar esta pequeña área, y todo lo demás permanece igual. Trabajo hecho.


Aún así, la ventaja de la base de texto de XML es que, si es necesario , puede ser leída y escrita por humanos (normalmente no molestan una vez que funciona, pero ciertamente lo hacen durante el desarrollo). En términos de rendimiento y eficiencia de espacio, los formatos binarios generalmente son mucho mejores (lo que a menudo no importa, porque el cuello de botella está en otro lugar).
Leftaroundabout

@leftaroundabout Si necesita ese rendimiento y eficiencia de espacio, seguro. La razón por la cual muchas aplicaciones han pasado a formatos basados ​​en XML en estos días es que el rendimiento y la eficiencia de espacio no son los criterios principales que alguna vez fueron, y el historial ha demostrado cuán mal se mantienen los formatos de archivos binarios. (¡Documentos antiguos de MS Word para un ejemplo clásico!) Sin embargo, el punto permanece: el texto es tan adecuado para que las computadoras lo lean como los humanos.
Graham

Claro, un formato binario mal diseñado en realidad puede funcionar peor que un formato de texto bien pensado, e incluso un formato binario decente a menudo no es mucho más compacto que XML con algún algoritmo de compresión de propósito general. En mi opinión, lo mejor de ambos mundos es utilizar una especificación legible por humanos a través de tipos de datos algebraicos, y generar automáticamente una representación binaria eficiente del AST de estos tipos. Ver, por ejemplo, la biblioteca plana .
Leftaroundabout

7

Una respuesta un poco más pragmática, centrándose en por qué y no en qué es y qué no es el código fuente. Tenga en cuenta que la generación de código fuente es parte del proceso de compilación en todos estos casos, por lo que los archivos generados no deberían llegar al control de origen.

Interoperabilidad / simplicidad

Tomemos los Protocol Buffers de Google, un buen ejemplo: usted escribe una única descripción de protocolo de alto nivel que luego puede usarse para generar la implementación en varios idiomas, a menudo diferentes partes del sistema se escriben en diferentes idiomas.

Implementación / razones técnicas

Tome TypeScript: los navegadores no pueden interpretarlo, por lo que el proceso de compilación utiliza un transpilador ( traductor de código a código) para generar JavaScript. De hecho, muchos lenguajes compilados nuevos o esotéricos comienzan con la transpiración a C antes de obtener un compilador adecuado.

Facilidad de uso

Para proyectos incrustados (piense en IoT) escritos en C y usando solo un solo binario (RTOS o sin SO), es bastante fácil generar una matriz C con los datos para compilar como si fuera un código fuente normal, en oposición a vincularlos directamente como recursos

Editar

Ampliación en protobuf: la generación de código permite que los objetos generados sean clases de primera clase en cualquier idioma. En un lenguaje compilado, un analizador genérico necesariamente devolvería una estructura clave-valor, lo que significa que necesita mucho código repetitivo, se perderá algunas comprobaciones en tiempo de compilación (en claves y tipos de valores en particular), obtendrá un peor rendimiento y Sin completar el código. Imagine todos aquellos void*en C o tan grandes std::varianten C ++ (si tiene C ++ 17), algunos lenguajes pueden no tener tal característica en absoluto.


Por la primera razón, creo que la idea del OP sería tener una implementación genérica en cada idioma (que toma la descripción de los buffers de protocolo y luego analiza / consume el formato en el cable). ¿Por qué sería esto peor que generar código?
Paŭlo Ebermann

@ PaŭloEbermann, aparte del argumento habitual de interpretación, una interpretación tan genérica haría imposible usar esos mensajes como objetos de primera clase en lenguajes compilados (y posiblemente interpretados), en C ++, por ejemplo, tal intérprete necesariamente devolvería una estructura de valores clave . Por supuesto, puede obtener ese kv en sus clases, pero puede convertirse en una gran cantidad de código repetitivo. Y también hay código de finalización también. Y la comprobación del tiempo de compilación: su compilador no comprobará si sus literales no tienen errores tipográficos.
Jan Dorniak

Estoy de acuerdo ... ¿podría agregar esto a la respuesta?
Paŭlo Ebermann

@ PaŭloEbermann hecho
Jan Dorniak

6

¿La generación del código fuente es un anti patrón?

Es una solución para un lenguaje de programación insuficientemente expresivo. No es necesario generar código en un lenguaje que contenga metaprogramación incorporada adecuada.


3
También es una solución para tener que escribir un compilador completo de código de objeto nativo para un lenguaje más expresivo. Genere C, deje que un compilador con un buen optimizador se encargue del resto.
Blrfl

No siempre. A veces tiene una o más bases de datos que contienen algunas definiciones para, por ejemplo, señales en un bus. Luego, desea reunir esta información, tal vez hacer algunas comprobaciones de consistencia y luego escribir código que interactúe entre las señales que provienen del bus y las variables que espera tener en su código. Si puede mostrarme un lenguaje que tenga metaprogramación que facilite el uso de algunas hojas de Excel proporcionadas por el cliente, una base de datos y otras fuentes de datos y cree el código que necesito, con algunas comprobaciones necesarias sobre la validez y coherencia de los datos, entonces todos los medios muéstrame.
CodeMonkey

@CodeMonkey: me viene a la mente algo como la implementación de ActiveRecord de Ruby on Rails. No es necesario duplicar el esquema de la tabla de la base de datos en el código. Simplemente asigne una clase a una tabla y escriba la lógica de negocios utilizando los nombres de las columnas como propiedades. No puedo imaginar ningún tipo de patrón que pueda ser producido por un generador de código que no pueda ser administrado también por la metaprogramación de Ruby. Las plantillas de C ++ también son extremadamente poderosas, aunque un poco arcanas. Las macros de Lisp son otro poderoso sistema de metaprogramación en lenguaje.
Kevin Cline

@kevincline lo que quise decir era código basado en algunos datos de la base de datos (podría construirse a partir de él), pero no en la base de datos en sí. Es decir, tengo información sobre qué señales recibo en Excel Tabla A. Tengo una Base de datos B con información sobre estas señales, etc. Ahora quiero tener una clase que acceda a estas señales. No hay conexión a la base de datos o la hoja de Excel en la máquina que ejecuta el código. Usando C ++ Templating realmente complicado para generar este código en tiempo de compilación, en lugar de un simple generador de código. Elegiré codegen.
CodeMonkey

6

La generación del código fuente no siempre es un antipatrón. Por ejemplo, actualmente estoy escribiendo un marco que, según la especificación dada, genera código en dos idiomas diferentes (Javascript y Java). El marco utiliza el Javascript generado para registrar las acciones del navegador del usuario, y usa el código Java en Selenium para ejecutar realmente la acción cuando el marco está en modo de reproducción. Si no usara la generación de código, tendría que asegurarme manualmente de que ambos estén siempre sincronizados, lo cual es engorroso y también es una duplicación lógica de alguna manera.

Sin embargo, si uno está utilizando la generación de código fuente para reemplazar características como los genéricos, entonces es antipatrón.


Podría, por supuesto, escribir su código una vez en ECMAScript y ejecutarlo en Nashorn o Rhino en la JVM. O bien, puede escribir una JVM en ECMAScript (o intentar compilar Avian to WebAssembly usando Emscripten) y ejecutar su código Java en el navegador. No digo que esas sean grandes ideas (bueno, probablemente son ideas terribles :-D), pero al menos son posibles si no factibles.
Jörg W Mittag

En teoría, es posible, pero no es una solución general. ¿Qué sucede si no puedo ejecutar uno de los idiomas dentro de otro? Por ejemplo, una cosa adicional: acabo de crear un modelo simple de Netlogo usando la generación de código y tengo una documentación interactiva del sistema, que siempre está sincronizada con la grabadora y el reproductor. Y en general, crear un requisito y luego generar código mantiene sincronizadas las cosas que se ejecutan semánticamente juntas.
Hristo Vrigazov

6

¿Me estoy perdiendo de algo?

¿Quizás un buen ejemplo donde el código intermediario resultó ser la razón del éxito? Puedo ofrecerte HTML.

Creo que era importante que el HTML fuera simple y estático: facilitaba la creación de navegadores, permitía iniciar navegadores móviles antes de tiempo, etc. Como demostraron más experimentos (applets de Java, Flash), los lenguajes más complejos y potentes conducen a más problemas . Resulta que los usuarios están realmente en peligro por los applets de Java y visitar esos sitios web era tan seguro como probar los juegos descargados a través de DC ++. El HTML simple, por otro lado, es lo suficientemente inofensivo como para permitirnos visitar cualquier sitio con una creencia razonable en la seguridad de nuestro dispositivo.

Sin embargo, HTML no estaría cerca de donde está ahora si no fuera generado por computadora. Mi respuesta ni siquiera aparecería en esta página hasta que alguien la reescribiera manualmente desde la base de datos en un archivo HTML. Afortunadamente, puedes hacer HTML utilizable en casi cualquier lenguaje de programación :)

Es decir, si hay un generador de código para algo, entonces ¿por qué no hacer de ese algo una función adecuada que pueda recibir los parámetros requeridos y realizar la acción correcta que el código "generado" habría hecho?

¿Te imaginas una mejor manera de mostrar la pregunta y todas las respuestas y comentarios al usuario que usando HTML como un código intermedio generado?


Sí, puedo imaginar una mejor manera. HTML es un legado de una decisión de Tim Berners-Lee de permitir la creación rápida de un navegador web de solo texto. Eso estaba perfectamente bien en ese momento, pero no haríamos lo mismo con el beneficio de la retrospectiva. CSS ha hecho innecesarios los diversos tipos de elementos de presentación (DIV, SPAN, TABLE, UL, etc.).
Kevin Cline

@kevincline No estoy diciendo que el HTML como tal no tenga fallas, estaba señalando que la introducción del lenguaje de marcado (que puede ser generado por un programa) funcionó muy bien en este caso.
Džuris

Entonces HTML + CSS es mejor que solo HTML. Incluso he escrito documentación interna para algunos proyectos en los que he trabajado directamente en HTML + CSS + MathJax. Pero la mayoría de las páginas web que visito parecen haber sido producidas por generadores de código.
David K

3

¿Por qué generar código fuente?

Porque es más rápido y fácil (y menos propenso a errores) que escribir el código manualmente, especialmente para tareas tediosas y repetitivas. También puede usar la herramienta de alto nivel para verificar y validar su diseño antes de escribir una sola línea de código.

Casos de uso común:

  • Herramientas de modelado como Rose o Visual Paradigm;
  • Alta er lenguajes de alto nivel como SQL incorporado o un lenguaje de definición de interfaz que deben ser pre-procesada en algo compilables;
  • Lexer y generadores de analizadores sintácticos como flex / bison;

En cuanto a su "por qué no simplemente convertirlo en una función y pasarle parámetros directamente", tenga en cuenta que ninguno de los anteriores son entornos de ejecución en sí mismos. No hay forma de vincular su código contra ellos.


2

A veces, su lenguaje de programación simplemente no tiene las instalaciones que desea, lo que hace que sea realmente imposible escribir funciones o macros para hacer lo que desea. O tal vez podrías hacer lo que quieras, pero el código para escribirlo sería feo. Un simple script de Python (o similar) puede generar el código requerido como parte de su proceso de compilación, que luego ingresa #includeen el archivo fuente real.

¿Cómo se esto? Porque es una solución a la que he llegado varias veces cuando trabajo con varios sistemas diferentes, más recientemente SourcePawn. Una secuencia de comandos Python simple que analiza una línea simple de código fuente y produce dos o tres líneas de código generado es mucho mejor que crear manualmente el código generado, cuando terminas con dos docenas de tales líneas (creando todos mis cvars).

Código fuente demostrativo / de ejemplo disponible si la gente lo quiere.


1

Se requiere forma de texto para que los humanos puedan consumirlo fácilmente. Las computadoras también procesan el código en forma de texto con bastante facilidad. Por lo tanto, el código generado debe generarse en la forma que sea más fácil de generar y más fácil de consumir por las computadoras, y que a menudo es texto legible.

Y cuando genera código, el proceso de generación de código en sí a menudo debe ser depurado, por humanos. Es muy, muy útil si el código generado es legible por humanos para que los humanos puedan detectar problemas en el proceso de generación de código. Alguien tiene que escribir el código para generar código, después de todo. No sucede de la nada.


1

Generando código, solo una vez

No toda la generación de código fuente es un caso de generar algún código, y luego nunca tocarlo; luego regenerarlo desde la fuente original cuando necesita actualizarse.

A veces genera código solo una vez, y luego descarta la fuente original, y en adelante mantiene la nueva fuente.

Esto a veces sucede cuando se transfiere código de un idioma a otro. Particularmente si uno no espera querer trasladar más adelante nuevos cambios en el original (por ejemplo, el código del idioma antiguo no se mantendrá, o en realidad está completo (por ejemplo, en el caso de alguna funcionalidad matemática)).

Un caso común es que escribir un generador de código para hacer esto podría traducir el 90% del código correctamente. y luego ese último 10% debe repararse a mano. Lo cual es mucho más rápido que traducir 100% a mano.

Tales generadores de código son a menudo muy diferentes al tipo de generadores de código que f2cproducen los traductores de lenguaje completo (como Cython o ). Dado que el objetivo es hacer mantener el código una vez. A menudo se hacen como 1 apagado, para hacer exactamente lo que tienen que hacer. En muchos sentidos, es la versión de siguiente nivel del uso de una expresión regular / buscar-reemplazar al código de puerto. "Portado asistido por herramientas" se podría decir.

Generando código, solo una vez, desde, por ejemplo, un raspado de sitio web.

Estrechamente relacionado es si genera el código de alguna fuente a la que no desea acceder nuevamente. Por ejemplo, si las acciones necesarias para generar el código no son repetibles o consistentes, o realizarlas es costoso. Estoy trabajando en un par de proyectos en este momento: DataDeps.jl y DataDepsGenerators.jl .

DataDeps.jl ayuda a los usuarios a descargar datos (como conjuntos de datos ML estándar). Para hacer esto necesita lo que llamamos un RegistrationBlock. Ese es un código que especifica algunos metadatos, como dónde descargar los archivos y una suma de verificación, y un mensaje que explica al usuario cualquier término / codificación / cuál es el estado de la licencia de los datos.

Escribir esos bloques puede ser molesto. Y esa información a menudo está disponible en (estructurada o no estructurada) en los sitios web donde se alojan los datos. Por lo tanto, DataDepsGenerators.jl utiliza un raspador web para generar el RegistrationBlockCode, para algunos sitios que alojan una gran cantidad de datos.

Puede que no los genere correctamente. Entonces, el desarrollador que usa el código generado puede y debe verificarlo y corregirlo. Lo más probable es que quieran asegurarse de que no haya descartado la información de licencia, por ejemplo.

Es importante destacar que los usuarios / desarrolladores que trabajan con DataDeps.jl no necesitan instalar o usar el webcraper para usar el código RegistrationBlock que se generó. (Y no necesitar descargar e instalar un raspador de web ahorra un poco de tiempo, particularmente para las ejecuciones de CI)

Generar código fuente una vez no es un antipatrón. y normalmente no se puede reemplazar con metaprogramación.


"informe" es una palabra en inglés que significa algo más que "puerto de nuevo". Intente "volver a informar" para aclarar esa oración. (Comentando porque es demasiado pequeño para una edición sugerida.)
Peter Cordes

Buena captura @PeterCordes que he reformulado.
Lyndon White el

Más rápido pero potencialmente mucho menos mantenible, dependiendo de cuán horrible sea el código generado. Fortran a C era algo antiguo en el día (los compiladores de C estaban más disponibles, por lo que las personas usarían f2c+ cc), pero el código resultante no era realmente un buen punto de partida para una versión en C del programa, AFAIK.
Peter Cordes el

1
Potencialmente, potencialmente no. No es culpa del concepto de generadores de código que algunos generadores de código creen código no mantenible. En particular, una herramienta hecha a mano, que no tiene que atrapar todos los casos, a menudo puede ser un código perfectamente agradable. Si el 90% del código es solo una lista de constantes de matriz, por ejemplo, generar esos constructores de matriz como una sola vez puede hacerse trivialmente muy bien y con poco esfuerzo. (Por otro lado, la salida del código C por Cython no puede ser mantenida por humanos. Porque no está destinada a serlo. Tal como usted lo dice en el f2cpasado)
Lyndon White

1
La gran mesa era simplemente el argumento más simple y reducido. Se puede decir lo mismo para, por ejemplo, convertir bucles for o condiciones. De hecho, sedva un largo camino, pero a veces uno necesita un poco más de poder expresivo. La línea entre la lógica del programa y los datos suele ser buena. A veces la distinción no es útil. JSON es (/ was) solo el código del constructor de objetos javascript. En mi ejemplo, yo también estoy generando código de constructor de objeto (es que los datos tal vez (tal vez no ya que a veces tiene llamadas de función) ¿Es mejor tratados como código de sí?.?.)
Lyndon blanca

1

La generación del código "fuente" es una indicación de una deficiencia del lenguaje que se genera. ¿Usar herramientas para superar esto es un antipatrón? Absolutamente no, déjame explicarte.

Por lo general, la generación de código se usa porque existe una definición de nivel superior que puede describir el código resultante mucho menos detallado que el lenguaje de nivel inferior. Por lo tanto, la generación de código facilita la eficiencia y la brevedad.

Cuando escribo c ++, lo hago porque me permite escribir código más eficiente que usar ensamblador o código de máquina. Todavía el código de máquina es generado por el compilador. Al principio, c ++ era simplemente un preprocesador que generaba código C. Los lenguajes de propósito general son excelentes para generar un comportamiento de propósito general.

De la misma manera, al usar un DSL (lenguaje específico de dominio) es posible escribir conciso, pero tal vez el código se constriñe a una tarea específica. Esto hará que sea menos complicado generar el comportamiento correcto del código. Recuerde que el código es medios para y al final . Lo que busca un desarrollador es una forma eficiente de generar comportamiento.

Idealmente, el generador puede crear código rápido a partir de una entrada que sea más fácil de manipular y comprender. Si esto se cumple, no usar un generador es un antipatrón . Este antipatrón generalmente proviene de la noción de que el código "puro" es "más limpio", de la misma manera que un trabajador de la madera u otro artesano podría considerar el uso de herramientas eléctricas o el uso de CNC para "generar" piezas de trabajo (piense en dorado martillo )

Por otro lado, si la fuente del código generado es más difícil de mantener o generar código que no es lo suficientemente eficiente, el usuario cae en la trampa de usar las herramientas incorrectas (en algún momento debido al mismo martillo dorado ).


0

La generación del código fuente absolutamente significa que el código generado son datos. Pero son datos de primera clase, datos que el resto del programa puede manipular.

Los dos tipos de datos más comunes que conozco que están integrados en el código fuente son la información gráfica sobre ventanas (número y ubicación de varios controles) y ORM. En ambos casos, la integración a través de la generación de código facilita la manipulación de los datos, ya que no tiene que pasar por pasos "especiales" adicionales para usarlos.

Al trabajar con las Macs originales (1984), las definiciones de diálogo y ventana se crearon utilizando un editor de recursos que mantuvo los datos en formato binario. Usar estos recursos en su aplicación fue más difícil de lo que hubiera sido si el "formato binario" hubiera sido Pascal.

Entonces, no, la generación del código fuente no es un antipatrón, permite que los datos formen parte de la aplicación, lo que facilita su uso.


0

La generación de código es un antipatrón cuando cuesta más de lo que logra. Esta situación ocurre cuando la generación se lleva a cabo de A a B, donde A es casi el mismo lenguaje que B, pero con algunas extensiones menores que podrían hacerse simplemente codificando en A con menos esfuerzo que todas las herramientas personalizadas y la preparación de etapas para A a B .

La compensación es más prohibitiva contra la generación de código en lenguajes que no tienen instalaciones de metaprogramación (macros estructurales) debido a las complicaciones e insuficiencias de lograr la metaprogramación a través de la puesta en escena del procesamiento de texto externo.

El intercambio pobre también podría tener que ver con la cantidad de uso. El lenguaje A podría ser sustancialmente diferente del B, pero todo el proyecto con su generador de código personalizado solo usa A en uno o dos lugares pequeños, de modo que la cantidad total de complejidad (pequeños bits de A, más el generador de código A -> B, más la puesta en escena circundante de construcción) excede la complejidad de una solución que se acaba de hacer en B.

Básicamente, si nos comprometemos con la generación de código, probablemente deberíamos "ir a lo grande o ir a casa": hacer que tenga una semántica sustancial, y usarla mucho, o no molestarnos.


¿Por qué eliminó el párrafo "Cuando Bjarne Stroustrup implementó por primera vez C ++ ..."? Creo que fue interesante
Utku

@Utku Otras respuestas cubren esto desde el punto de vista de compilar un lenguaje completo y sofisticado, en el que el resto del proyecto está completamente escrito. No creo que sea representativo de la mayoría de lo que se llama "generación de código".
Kaz

0

No vi esto claramente establecido (lo vi tocado por una o dos respuestas, pero no parecía muy claro)

Generar código (como dijiste, como si fueran datos) no es un problema, es una forma de reutilizar un compilador para un propósito secundario.

Editar el código generado es uno de los antipatrones más insidiosos, malvados y horribles que jamás haya encontrado. No hagas esto.

En el mejor de los casos, la edición del código generado extrae un montón de código deficiente en su proyecto (el conjunto COMPLETO de código ahora es realmente CÓDIGO FUENTE - ya no son datos). En el peor de los casos, el código extraído en su programa es altamente redundante, basura mal nombrada que es casi completamente imposible de mantener.

Supongo que una tercera categoría es el código que usa una vez (¿generador de interfaz gráfica de usuario?) Y luego edite para ayudarlo a comenzar / aprender. Esto es un poco de cada uno: PUEDE ser una buena manera de comenzar, pero su generador de GUI estará destinado a usar código "Generable" que no será un gran comienzo para usted como programador. Además, puede ser Está tentado a usarlo nuevamente para una segunda GUI, lo que significa introducir código SOURCE redundante en su sistema.

Si su herramienta es lo suficientemente inteligente como para no permitir ninguna edición del código generado, hágalo. Si no, lo llamaría uno de los peores antipatrones que existen.


0

El código y los datos son: información.

Los datos son la información exactamente en la forma que necesita (y valor). El código también es información, pero de forma indirecta o intermedia. En esencia, el código también es una forma de datos.

Más específicamente, el código es información para que las máquinas descarguen a los humanos del procesamiento de la información por sí mismos.

Descargar a los humanos del procesamiento de la información es el motivo más importante. Los pasos intermedios son aceptables siempre que faciliten la vida. Es por eso que existen herramientas intermedias de mapeo de información. Como generadores de código, compiladores, transpiladores, etc.

¿Por qué generar código fuente? ¿Por qué no convertirlo en una función que pueda aceptar parámetros y actuar sobre ellos?

Digamos que alguien le ofrece dicha función de mapeo, cuya implementación es oscura para usted. Mientras la función funcione según lo prometido, ¿le importaría si internamente genera código fuente o no?


0

Si se puede generar algo, entonces eso es información, no código.

En la medida en que estipule más adelante que ese código son datos, su propuesta se reduce a "Si se puede generar algo, entonces esa cosa no es código". ¿Diría, entonces, que el código de ensamblaje generado por un compilador de C no es código? ¿Qué pasa si coincide exactamente con el código de ensamblaje que escribo a mano? Puedes ir allí si lo deseas, pero no iré contigo.

Comencemos con una definición de "código". Sin ser demasiado técnico, una definición bastante buena para los propósitos de esta discusión sería "instrucciones accionables por la máquina para realizar un cálculo".

Dado eso, ¿no es toda esta idea de generación de código fuente un malentendido?

Bueno, sí, su propuesta inicial es que el código no se puede generar, pero rechazo esa propuesta. Si acepta mi definición de "código", entonces no debería haber ningún problema conceptual con la generación de código en general.

Es decir, si hay un generador de código para algo, entonces ¿por qué no hacer de ese algo una función adecuada que pueda recibir los parámetros requeridos y realizar la acción correcta que el código "generado" habría hecho?

Bueno, esa es una pregunta completamente diferente, sobre la razón para emplear la generación de código, en lugar de sobre su naturaleza. Está proponiendo la alternativa de que, en lugar de escribir o usar un generador de código, se escriba una función que calcule el resultado directamente. ¿Pero en qué idioma? Atrás quedaron los días en que alguien escribió directamente en el código de máquina, y si escribe su código en cualquier otro idioma, entonces depende de un generador de código en forma de compilador y / o ensamblador para producir un programa que realmente se ejecute.

¿Por qué, entonces, prefieres escribir en Java o C o Lisp o lo que sea? Incluso ensamblador? Afirmo que es al menos en parte porque esos lenguajes proporcionan abstracciones para los datos y las operaciones que hacen que sea más fácil expresar los detalles del cálculo que desea realizar.

Lo mismo es cierto para la mayoría de los generadores de código de nivel superior, también. Los casos prototípicos son probablemente generadores de escáner y analizador sintáctico como lexy yacc. Sí, puede escribir un escáner y un analizador directamente en C o en algún otro lenguaje de programación de su elección (incluso código máquina sin formato), y a veces uno lo hace. Pero para un problema de complejidad significativa, el uso de un lenguaje de propósito especial de nivel superior como lex's o yacc's hace que el código escrito a mano sea más fácil de escribir, leer y mantener. Por lo general, también es mucho más pequeño.

También debe considerar qué quiere decir exactamente con "generador de código". Consideraría el preprocesamiento de C y la creación de instancias de plantillas de C ++ como ejercicios en la generación de código; ¿te opones a esto? Si no, entonces creo que necesitarás realizar algunas gimnasias mentales para racionalizar la aceptación de esas pero rechazando otros sabores de generación de código.

Si se hace por razones de rendimiento, eso suena como una deficiencia del compilador.

¿Por qué? Básicamente está postulando que uno debería tener un programa universal al que el usuario alimente datos, algunos clasificados como "instrucciones" y otros como "entrada", y que proceda a realizar el cálculo y emitir más datos que llamamos "salida". (Desde cierto punto de vista, uno podría llamar a un programa tan universal como "sistema operativo"). Pero, ¿por qué supone que un compilador debería ser tan efectivo para optimizar un programa de propósito general como lo es para optimizar un programa más especializado? ¿programa? Los dos programas tienen características diferentes y capacidades diferentes.

Si se está haciendo para unir dos idiomas, entonces eso suena como una falta de biblioteca de interfaz.

Dices eso como si tener una biblioteca de interfaz universal en algún grado fuera necesariamente algo bueno. Quizás lo haría, pero en muchos casos una biblioteca de este tipo sería grande y difícil de escribir y mantener, y tal vez incluso lenta. Y si tal bestia, de hecho, no existe para atender el problema particular en cuestión, ¿quién es usted para insistir en que se cree uno, cuando un enfoque de generación de código puede resolver el problema mucho más rápida y fácilmente?

¿Me estoy perdiendo de algo?

Varias cosas, creo.

Sé que el código también es información. Lo que no entiendo es, ¿por qué generar código fuente? ¿Por qué no convertirlo en una función que pueda aceptar parámetros y actuar sobre ellos?

Los generadores de código transforman el código escrito en un idioma para codificar en un idioma diferente, generalmente de nivel inferior. Se pregunta, entonces, por qué la gente querría escribir programas usando múltiples idiomas, y especialmente por qué querrían mezclar idiomas de niveles subjetivamente diferentes.

Pero ya toqué eso. Uno elige un lenguaje para una tarea particular basado en parte en su claridad y expresividad para esa tarea. Como el código más pequeño tiene menos errores en promedio y es más fácil de mantener, también existe un sesgo hacia los lenguajes de nivel superior, al menos para el trabajo a gran escala. Pero un programa complejo implica muchas tareas y, a menudo, algunas de ellas pueden abordarse de manera más efectiva en un idioma, mientras que otras se abordan de manera más eficaz o más concisa en otro. Usar la herramienta adecuada para el trabajo a veces significa emplear la generación de código.


0

Respondiendo la pregunta dentro del contexto de tu comentario:

El deber del compilador es tomar un código escrito en forma legible por humanos y convertirlo en una forma legible por máquina. Por lo tanto, si el compilador no puede crear un código que sea eficiente, entonces el compilador no está haciendo su trabajo correctamente. ¿Es eso incorrecto?

Un compilador nunca estará optimizado para su tarea. La razón de esto es simple: está optimizado para hacer muchas tareas. Es una herramienta de uso general utilizada por muchas personas para muchas tareas diferentes. Una vez que sepa cuál es su tarea, puede abordar el código de una manera específica del dominio, haciendo compensaciones que los compiladores no podrían.

Como ejemplo, he trabajado en software en el que un analista puede necesitar escribir algún código. Podrían escribir su algoritmo en C ++ y agregar todas las comprobaciones de límites y trucos de memorización de los que dependen, pero eso requiere saber mucho sobre el funcionamiento interno del código. Prefieren escribir algo simple y dejarme lanzar un algoritmo para generar el código final de C ++. Entonces puedo hacer trucos exóticos para maximizar el rendimiento como el análisis estático que nunca esperaría que soportaran mis analistas. La generación de código les permite escribir de una manera específica del dominio, lo que les permite sacar el producto de la puerta más fácilmente que cualquier herramienta de propósito general.

También he hecho exactamente lo contrario. Tengo otro trabajo que hice que tenía el mandato de "no generar código". Todavía queríamos facilitarles la vida a quienes usan el software, por lo que utilizamos cantidades masivas de metaprogramación de plantillas para hacer que el compilador genere el código sobre la marcha. Por lo tanto, solo necesitaba el lenguaje C ++ de propósito general para hacer mi trabajo.

Sin embargo, hay una trampa. Fue tremendamente difícil garantizar que los errores fueran legibles. Si alguna vez ha usado código metaprogramado de plantilla anteriormente, sabe que un solo error inocente puede generar un error que requiere 100 líneas de nombres de clase incomprensibles y argumentos de plantilla para comprender qué salió mal. Este efecto fue tan pronunciado que el proceso de depuración recomendado para errores de sintaxis fue "Desplácese por el registro de errores hasta que vea la primera vez que uno de sus propios archivos tiene un error. Vaya a esa línea y solo bíjela hasta que se dé cuenta de lo que hizo mal ".

Si hubiéramos utilizado la generación de código, podríamos haber tenido capacidades de manejo de errores mucho más poderosas, con errores legibles por humanos. Así es la vida.


0

Hay algunas formas diferentes de usar la generación de código. Podrían dividirse en tres grupos principales:

  • Generando código en un idioma diferente como resultado de un paso en el proceso de compilación. Para el compilador típico, este sería un lenguaje de nivel inferior, pero podría ser para otro lenguaje de alto nivel como en el caso de los lenguajes que compilan a JavaScript.
  • Generar o transformar código en el lenguaje de código fuente como un paso en el proceso de compilación. Esto es lo que hace las macros .
  • Generando código con una herramienta separada del proceso de compilación regular. El resultado de esto es el código que vive como archivos junto con el código fuente normal y se compila junto con él. Por ejemplo, las clases de entidad para un ORM pueden generarse automáticamente a partir de un esquema de base de datos, o los objetos de transferencia de datos y las interfaces de servicio pueden generarse a partir de una especificación de interfaz como un archivo WSDL para SOAP.

Supongo que estás hablando del tercer tipo de código generado, ya que esta es la forma más controvertida. En las dos primeras formas, el código generado es un paso intermedio que está muy limpio del código fuente. Pero en la tercera forma no hay una separación formal entre el código fuente y el código generado, excepto que el código generado probablemente tenga un comentario que diga "no edite este código". Todavía abre el riesgo de que los desarrolladores editen el código generado que sería realmente feo. Desde el punto de vista del compilador, el código generado es el código fuente.

Sin embargo, tales formas de código generado pueden ser realmente útiles en un lenguaje de tipo estático. Por ejemplo, cuando se integra con entidades ORM, es realmente útil tener contenedores fuertemente tipados para las tablas de la base de datos. Seguro que podría manejar la integración dinámicamente en tiempo de ejecución, pero perdería seguridad de tipo y soporte de herramientas (finalización de código). Una ventaja importante del lenguaje de tipos estático es el soporte del sistema de tipos en el tipo de escritura en lugar de solo en tiempo de ejecución. (Por el contrario, este tipo de generación de código no es muy frecuente en los idiomas de tipo dinámico, ya que en dicho lenguaje no proporciona ningún beneficio en comparación con las conversiones en tiempo de ejecución).

Es decir, si hay un generador de código para algo, entonces ¿por qué no hacer de ese algo una función adecuada que pueda recibir los parámetros requeridos y realizar la acción correcta que el código "generado" habría hecho?

Debido a que la seguridad de tipos y la finalización del código son características que desea en el momento de la compilación (y al escribir código en un IDE), pero las funciones regulares solo se ejecutan en tiempo de ejecución.

Sin embargo, puede haber un punto medio: F # admite el concepto de proveedores de tipos, que básicamente son interfaces fuertemente tipadas generadas mediante programación en tiempo de compilación. Este concepto probablemente podría reemplazar muchos usos de la generación de código y proporcionar una separación más clara de las preocupaciones.


0

Los conjuntos de instrucciones del procesador son fundamentalmente imprescindibles , pero los lenguajes de programación pueden ser declarativos . Ejecutar un programa escrito en un lenguaje declarativo inevitablemente requiere algún tipo de generación de código. Como se menciona en esta respuesta y en otras, una de las principales razones para generar código fuente en un lenguaje legible por humanos es aprovechar las sofisticadas optimizaciones realizadas por los compiladores.


-3

Si se puede generar algo, entonces eso es información, no código.

Lo entendiste al revés. Debería leer

Si algo se puede alimentar a un generador para interpretables , entonces esa cosa es código, no datos.

Es el formato fuente para esa etapa de compilación, y el formato sumidero sigue siendo código.


1
Definición incorrecta del código fuente . El código fuente es principalmente para humanos que trabajan en él (y ese simple hecho lo define, vea también qué es el software libre de la FSF). El código de ensamblador generado con gcc -fverbose-asm -O -Sno es el código fuente (y no es solo o principalmente datos), incluso si se trata de alguna forma textual siempre alimentada a GNU asy a veces leída por humanos.
Basile Starynkevitch

Además, muchas implementaciones de lenguajes compilan en código C , pero ese C generado no es un código fuente genuino (por ejemplo, no puede ser trabajado fácilmente por humanos).
Basile Starynkevitch

Por fin, su hardware (por ejemplo, su chip AMD o Intel, o la placa base de su computadora) está interpretando el código de la máquina (que obviamente no es el código fuente). Por cierto, el IBM1620 tenía un código de máquina de teclado (BCD), pero ese hecho no lo convirtió en "código fuente". Todo el código no es fuente.
Basile Starynkevitch

@BasileStarynkevitch Ah, me tienes allí. No debería tratar de comprimir demasiado mi ingeniosa declaración, o cambiarán su significado. Correcto, el código fuente debe ser el código más original que se introduce en la primera etapa de compilación.
Bergi

Ningún código fuente es código para humanos. Es tan difícil y subjetivo definirlo como música (vs. sonido). No se trata de tratar de encontrar el software que lo consume.
Basile Starynkevitch
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.