compilation - Ingeniería de software con Ada: stubs; unidades separadas y de compilación
(3)
Es bastante normal dividir un problema en partes componentes (paquetes), cada uno de los cuales soporta un aspecto diferente. Si aprendió Ada, sería normal escribir las especificaciones de los paquetes primero, discutir (quizás con usted mismo) por qué ese es el diseño correcto y luego implementarlos. Y esto sería normal, creo, en cualquier idioma que admita especificaciones y cuerpos, por ejemplo, C.
Personalmente verificaba compilaciones a medida que avanzaba, solo para asegurarme de no estar haciendo nada estúpido.
En cuanto a las separaciones, una razón (no muy buena) es reducir el desorden, para evitar que la unidad se alargue demasiado. Otra razón (para un generador de código que escribí) era que el generador de código no tenía que preocuparse por preservar el código escrito a mano de los desarrolladores en el modelo UML; todos los cuerpos de código estaban separados. Un tercero podría ser para la implementación dependiente del entorno (por ejemplo, Windows vs Unix), donde permitiría que el compilador vea una versión diferente del cuerpo separado para cada entorno (sin embargo, las personas normalmente usan paquetes de biblioteca para esto).
Los compiladores tienen sus propias reglas sobre los nombres de los archivos y en qué orden se pueden compilar las cosas. Cuando GNAT ve
procedure Foo is
procedure Bar is separate;
espera encontrar el cuerpo de Foo en un archivo llamado foo.adb
y el cuerpo de Bar en foo-bar.adb
(puedes, creo, decirlo diferente, el gnatmake
del paquete de gnatmake
, pero probablemente no valga la pena). Lo mejor es ir con el flujo aquí;
separate (Foo)
procedure Bar is
es lo suficientemente claro.
Puede compilar foo-bar.adb
, y eso hará un análisis completo y detectará casi todos los errores en el código; pero GNAT no puede generar código para esto solo. En cambio, cuando compila foo.adb
, incluye todos los cuerpos separados en el archivo de un objeto generado. Ciertamente no está mal hacer esto.
Con GNAT, no hay necesidad de preocuparse por el orden de compilación, puede compilar en el orden que desee. ¡Pero es mejor usar gnatmake
y dejar que la computadora tome la tensión!
Tengo experiencia en ingeniería mecánica, pero estoy interesado en aprender buenas prácticas de ingeniería de software con Ada. Tengo algunas preguntas
Q1. Si entiendo correctamente, alguien puede simplemente escribir un archivo de especificaciones del paquete (anuncios), compilarlo y luego compilar el programa principal que está usando el paquete. Más adelante, cuando uno sabe qué incluir en el cuerpo del paquete, este último puede escribirse y compilarse. Luego, el programa principal ahora puede ejecutarse. Lo intenté y me gustaría confirmar que esta es una buena práctica.
Q2. Mi segunda pregunta es sobre los stubs (subunidades) y el uso de SEPARATE. Digamos que tengo un programa principal de la siguiente manera:
WITH Ada.Float_Text_IO;
WITH Ada.Text_IO;
WITH Ada.Integer_Text_IO;
PROCEDURE TEST2 IS
A,B : FLOAT;
N : INTEGER;
PROCEDURE INPUT(A,B: OUT FLOAT; N: OUT INTEGER) IS SEPARATE;
BEGIN -- main program
INPUT(A,B,N);
Ada.Float_Text_IO.Put(Item => A);
Ada.Text_IO.New_line;
Ada.Integer_Text_IO.Put(Item => N);
END TEST2;
Luego tengo el procedimiento INPUT en un archivo separado:
separate(TEST2)
PROCEDURE INPUT(A,B: OUT FLOAT; N: OUT INTEGER) IS
BEGIN
Ada.Float_Text_IO.Get(Item => A);
Ada.Text_IO.New_line;
Ada.Float_Text_IO.Get(Item => B);
Ada.Text_IO.New_line;
Ada.Integer_Text_IO.Get(Item => N);
END INPUT;
Mis preguntas:
a) AdaGIDE me sugiere que guarde el archivo de procedimiento INPUT como input.adb. Pero luego de compilar el programa principal test2, recibo la advertencia:
warning: subunit "TEST2.INPUT" in file "test2-input.adb" not found
cannot generate code for file test2.adb (missing subunits)
Para AdaGIDE, esto es más un error ya que las advertencias anteriores aparecen antes del mensaje:
Compiling...
Done--error detected
Así que cambié el nombre del archivo input.adb a test2-input.adb como me sugirió AdaGIDE en la compilación. Ahora, al compilar el archivo principal, no tengo ninguna advertencia. Mi pregunta ahora es si está bien escribir
PROCEDURE INPUT(A,B: OUT FLOAT; N: OUT INTEGER) IS
como lo hice en el archivo de subunidad test2-input.adb o es mejor escribir un término más descriptivo como
PROCEDURE TEST2-INPUT(A,B: OUT FLOAT; N: OUT INTEGER) IS
para enfatizar que la entrada de procedimiento tiene un procedimiento principal test2? Este pensamiento se sigue de AdaGIDE que me insinúa sobre test2-input.adb como mencioné anteriormente.
b) Mi siguiente pregunta:
Si entiendo bien el orden de compilación, entonces debo compilar primero el archivo principal test2.adb y luego el apéndice test2-input.adb. Al compilar el código auxiliar, aparece el mensaje de error:
cannot generate code for file test2-input.adb (subunit)
Done--error detected
Sin embargo, ahora puedo hacer el enlace y el enlace para test2.adb y ejecutar el programa.
Me gustaría saber si hice mal al intentar compilar el stub test2-input.adb o no debería compilarse?
Q3. ¿De qué sirve tener subunidades? ¿Es solo para dividir un gran programa en partes más pequeñas? Sé que se produce un error si uno no pone ninguna declaración entre BEGIN y END en la subunidad. Entonces esto significa que uno siempre tiene que poner una declaración allí. Y si uno quiere escribir las declaraciones más tarde, siempre se puede poner una instrucción NULL entre BEGIN y END en la subunidad y vuelve a la última en un momento posterior. ¿Es así como se hace la ingeniería de software en la práctica?
Muchas gracias...
Q1: Esa es una excelente práctica.
Y al tratar la especificación del paquete como una especificación , puede proporcionarla a otros desarrolladores para que sepan cómo interactuar con su código.
P2: Creo que AdaGIDE realmente usa el compilador GNAT para toda la compilación, por lo que en realidad es GNAT quien está a cargo de los nombres de archivo aceptables. (Esto se puede configurar, pero a menos que tenga una razón muy convincente para hacerlo, es mucho más simple simplemente ir con las convenciones de nombres de archivo de GNAT / AdaGIDE.) Sin embargo, más pertinente a su pregunta, no hay una razón fuerte para incluir el padre unidad como parte del nombre de la unidad separada. Pero mira la respuesta a Q3 ...
Q3: Subunidades se introdujeron con la primera versión de Ada - Ada 83 - en parte para ayudar a modularizar el código y permitir el desarrollo diferido y la compilación. Sin embargo, la práctica de desarrollo de software de Ada ha abandonado prácticamente el uso de subunidades, todos los procedimientos / funciones / tareas / etc. los cuerpos simplemente se mantienen en el cuerpo del paquete. Todavía se usan en algunas áreas, como si se necesitara una versión de subprograma específica de la plataforma, pero en su mayor parte rara vez se usan. Deja menos archivos para realizar un seguimiento y mantiene el código de implementación de un paquete. Por lo tanto, le recomiendo que simplemente ignore las capacidades de la subunidad y coloque todo su código de implementación en los cuerpos del paquete.
De hecho, puede trabajar de la manera que describe, excepto, por supuesto, que su programa no se vinculará hasta que todos los cuerpos del paquete tengan algún tipo de implementación. Por esa razón, creo que es más normal escribir un cuerpo de paquete ficticio con todos los procedimientos implementados como:
begin
null;
end;
Y todas las funciones implementadas como algo así como:
begin
return The_Return_Type''first; --''
end;
En cuanto a las separa ... No me gustan. Para mí, prefiero poder seguir la regla de que todo el código de un paquete está en su cuerpo de paquete. Los separadores son marginalmente aceptables si por alguna razón la rutina es enorme , pero en ese caso, una solución mejor es casi siempre refactorizar su código. Entonces, cada vez que veo uno, es una gran bandera roja.
En cuanto a la cosa de nombre de archivo, este es un problema de mosquito, no un problema de Ada. Gnat tomó la posición inusual para un compilador de que el nombre del contenido de un archivo determina qué nombre debe tener el archivo. Probablemente haya otros compiladores en el mundo que lo hagan, pero todavía tengo que encontrar uno en 30 años de codificación.