module - Diferencia entre INCLUDE y módulos en Fortran
(3)
¿Cuáles son las diferencias prácticas entre usar módulos con la declaración de use
o archivos aislados con la instrucción include
? Quiero decir, si tengo una subrutina que se usa mucho a lo largo de un programa: cuándo o por qué debería ponerlo dentro de un módulo o simplemente escribirlo en un archivo separado e incluirlo en cualquier otra parte del programa donde sea necesario. ¿usado?
Además, ¿sería una buena práctica escribir todas las subrutinas destinadas a ir en un módulo en archivos separados y usar include
dentro del módulo? Especialmente si el código en las subrutinas es largo, a fin de mantener el código mejor organizado (de esa forma todas las subrutinas están empaquetadas en el mod, pero si tengo que editar una, no necesito atravesar un laberinto de código).
Colocar procedimientos en módulos y usar esos módulos hace que la interfaz del procedimiento sea explícita. Permite a un compilador de Fortran verificar la coherencia entre los argumentos reales en una llamada y los argumentos ficticios del procedimiento. Esto protege contra una variedad de errores del programador. También es necesaria una interfaz explícita para ciertas características "avanzadas" de Fortran> = 90; por ejemplo, argumentos opcionales o palabras clave. Sin la interfaz explícita, el compilador no generará la llamada correcta. Simplemente incluir un archivo no proporciona estas ventajas.
La respuesta de MSB es excelente y es probablemente la razón más importante para preferir módulos que incluir. Me gustaría agregar algunos pensamientos más.
Usar módulos reduce el tamaño binario compilado si eso es algo que es importante para usted. Un módulo se compila una vez, y cuando lo use
, está cargando simbólicamente ese módulo para usar el código. Cuando include
un archivo, en realidad está insertando el nuevo código en su rutina. Si usa include
mucho, puede hacer que su binario sea grande y también aumentar su tiempo de compilación.
También puede usar módulos para falsificar la codificación de estilo OOP en Fortran 90 a través del uso inteligente de las funciones públicas y privadas y los tipos definidos por el usuario en un módulo. Incluso si no quería hacer eso, ofrece una buena manera de agrupar funciones que lógicamente pertenecen juntas.
Las diferencias conceptuales entre los dos se traducen en diferencias prácticas muy significativas.
Una línea INCLUDE opera en el nivel fuente: logra la inclusión de texto simple ("tonto"). A falta de una interpretación especial del procesador del "nombre de archivo" (no es necesario que sea realmente un archivo) en la línea de inclusión, el programador pudo empalmar manualmente la fuente completa y alimentar al compilador sin diferencia con -siempre en la semántica de la fuente. La fuente incluida no tiene una interpretación real aislada: su significado depende por completo del contexto en el que aparece la línea de inclusión que hace referencia a la fuente incluida.
Los módulos operan en el nivel de entidad mucho más alto del programa, es decir, en el nivel donde el compilador está considerando las cosas que la fuente realmente describe. Se puede compilar un módulo en forma aislada de sus usuarios intermedios y una vez que se ha compilado, el compilador sabe exactamente qué cosas puede proporcionar el módulo al programa.
Por lo general, lo que alguien que utiliza líneas de inclusión espera hacer es qué módulos fueron realmente diseñados para hacer.
Ejemplos de problemas:
Debido a que las declaraciones de las entidades pueden distribuirse en varias declaraciones, las entidades descritas por la fuente incluida pueden no ser las esperadas. Considere la siguiente fuente para ser incluida:
INTEGER :: i
De forma aislada, parece que esto declara el nombre
i
como un escalar entero (¿o tal vez una función? ¿Quién sabe?). Ahora considere el siguiente alcance que incluye lo anterior:INCLUDE "source from above"
DIMENSION :: i(10,10)
i
Ahora soy una matriz de rango dos! ¿Quizás quieras convertirlo en un PUNTERO? Un ALLOCATABLE? ¿Una discusión falsa? Quizás eso resulte en un error, ¡o tal vez sea una fuente válida! Ponga tipeo implícito en la mezcla para realmente aumentar la diversión potencial.Una entidad definida en un módulo está "completamente" definida por el módulo. Los atributos que son específicos del alcance de uso se pueden cambiar (VOLATILE, accesibilidad, etc.), pero la entidad fundamental sigue siendo la misma. Los conflictos de nombres se llaman explícitamente y se pueden solucionar fácilmente con una cláusula de redenominación en la declaración USE.
Fortran tiene restricciones en el orden de las declaraciones (las declaraciones de especificaciones deben ir antes de las declaraciones ejecutables, etc.). La fuente incluida también está sujeta a esas restricciones, nuevamente en el contexto del punto de inclusión , no del punto de definición de la fuente.
Mezcle bien con la ambigüedad de origen entre las definiciones de función de declaración (parte de especificación) y las instrucciones de asignación (parte ejecutable) para algunos mensajes de error completamente obtusos o, peor aún, aceptación silenciosa por parte del compilador de código erróneo.
Existen requisitos sobre dónde aparece la declaración de USO que hace referencia a un módulo, pero la fuente de la unidad de programa de módulo real es completamente independiente de su punto de uso.
¿Te apetece tener algún estado global para ser compartido a través de los procedimientos relacionados y deseas usar incluir? Permítanme presentarles bloques comunes y el concepto subyacente asociado de asociación de secuencia ...
La asociación de secuencia es un desafortunado derrame de principios de la implementación del procesador Fortran que es un anacronismo propenso a errores, inflexible y anti optimización.
Las variables del módulo hacen que los bloques comunes y sus males asociados sean completamente innecesarios.
Si usaba líneas de inclusión, tenga en cuenta que en realidad no incluye el origen de un procedimiento comúnmente utilizado (la sugerencia en su primer párrafo va a resultar en una avalancha de errores de sintaxis del compilador). Lo que normalmente haría es incluir una fuente que describa la interfaz del procedimiento. Para cualquier procedimiento no trivial, la fuente que describe la interfaz es diferente de la fuente completa del procedimiento, lo que implica que ahora necesita mantener dos representaciones de origen de la misma cosa. Esta es una carga de mantenimiento propensa a errores.
Como se mencionó, los compiladores adquieren automáticamente conocimiento de la interfaz de un procedimiento de módulo (el conocimiento del compilador es "explícito" porque realmente vio el código del procedimiento, de ahí el término "interfaz explícita"). No es necesario que el programador haga nada más.
Una consecuencia de lo anterior es que los subprogramas externos no deberían utilizarse en absoluto a menos que haya muy buenas razones para lo contrario (tal vez la existencia de dependencias circulares o excesivamente extensas) - el punto de partida básico debería ser poner todo en un módulo o principal programa.
Otros carteles han mencionado los beneficios de la organización de los códigos fuente de los módulos, incluida la capacidad de agrupar los procedimientos relacionados y otras "cosas" en un solo paquete, con control sobre la accesibilidad de los detalles de la implementación interna.
Acepto que existe un uso válido de las líneas INCLUDE según el segundo párrafo de la pregunta, donde los módulos grandes se vuelven difíciles de manejar. F2008 ha abordado esto con submódulos, que también brindan una serie de otros beneficios. Una vez que reciban un amplio respaldo, se debe abandonar la línea de trabajo de inclusión.
Un segundo uso válido es superar la falta de soporte del lenguaje para las técnicas de programación genéricas (qué plantillas proporcionan en C ++), es decir, dónde pueden variar los tipos de objetos involucrados en una operación, pero la secuencia de tokens que describe qué hacer en esas los objetos son esencialmente lo mismo. Puede pasar una década más o menos antes de que el lenguaje lo solucione.