Compilation Ada软件工程:stubs;独立和汇编单位

Compilation Ada软件工程:stubs;独立和汇编单位,compilation,ada,stub,Compilation,Ada,Stub,我有机械工程背景,但我有兴趣学习Ada良好的软件工程实践。我有几个问题 问题1。如果我理解正确,那么某人可以编写一个包规范(ads)文件,编译它,然后编译使用该包的主程序。稍后,当您知道包体中包含什么时,就可以编写和编译包体。之后,现在可以运行主程序。我已经试过了,我想确认这是一个很好的实践 问题2。我的第二个问题是关于存根(子单元)和使用独立单元。假设我有一个如下的主程序: WITH Ada.Float_Text_IO; WITH Ada.Text_IO; WITH A

我有机械工程背景,但我有兴趣学习Ada良好的软件工程实践。我有几个问题

问题1。如果我理解正确,那么某人可以编写一个包规范(ads)文件,编译它,然后编译使用该包的主程序。稍后,当您知道包体中包含什么时,就可以编写和编译包体。之后,现在可以运行主程序。我已经试过了,我想确认这是一个很好的实践

问题2。我的第二个问题是关于存根(子单元)和使用独立单元。假设我有一个如下的主程序:

    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;
然后我将程序输入到一个单独的文件中:

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;
我的问题是:

a) AdaGIDE建议我将输入过程文件保存为INPUT.adb。但是在编译主程序test2时,我得到了警告:

warning: subunit "TEST2.INPUT" in file "test2-input.adb" not found
cannot generate code for file test2.adb (missing subunits)
对于AdaGIDE来说,这更像是一个错误,因为上述警告出现在消息之前:

Compiling...
Done--error detected
因此,我将input.adb文件重命名为test2-input.adb,正如AdaGIDE在编译时建议的那样。现在编译主文件时,我没有任何警告。我现在的问题是是否可以写

PROCEDURE INPUT(A,B: OUT FLOAT; N: OUT INTEGER) IS
正如我在子单元文件test2-input.adb中所做的那样,还是最好编写一个更具描述性的术语,如

PROCEDURE TEST2-INPUT(A,B: OUT FLOAT; N: OUT INTEGER) IS
为了强调过程输入有一个父过程test2?正如我前面提到的,AdaGIDE向我暗示test2-input.adb就是这种想法

b) 我的下一个问题:

如果我很好地理解编译顺序,那么我应该首先编译主文件test2.adb,然后编译存根test2-input.adb。编译存根时,我收到错误消息:

cannot generate code for file test2-input.adb (subunit)
Done--error detected
但是,我现在可以为test2.adb进行绑定和链接,并运行该程序

我想知道我是否错误地编译了存根test2-input.adb,还是应该不编译它

第三季度。拥有亚单位有什么用?只是为了把一个大程序分解成更小的部分吗?我知道如果没有在子单元的开始和结束之间放置任何语句,就会出现错误。所以这意味着你必须在那里写一个陈述。如果要稍后编写语句,可以在子单元的BEGIN和END之间放置一个NULL语句,稍后再返回到后者。这就是软件工程在实践中的做法吗

非常感谢……

Q1:这是一个很好的练习

通过将包规范视为规范,您可以将其提供给其他开发人员,以便他们知道如何与您的代码交互

问题2:我相信AdaGIDE实际上在所有编译中都使用GNAT编译器,所以实际上是GNAT负责可接受的文件名。(这是可以配置的,但除非您有非常令人信服的理由这样做,否则使用GNAT/AdaGIDE的文件命名约定要简单得多。)不过,与您的问题更相关的是,没有充分的理由将父单元作为单独单元名称的一部分。但请看第三季度的答案


问题3:Ada的第一个版本Ada 83引入了子单元,部分是为了帮助模块化代码,并允许延迟开发和编译。然而,Ada软件开发实践几乎放弃了子单元的使用,所有的过程/功能/任务/etc主体都简单地维护在包的主体中。它们仍然在某些领域使用,比如可能需要特定于平台的子程序版本,但在大多数情况下很少使用。它会留下更少的文件来跟踪,并将包的实现代码放在一起。因此,我强烈建议您忽略子单元功能,将所有实现代码放在包体中。

将问题分解为组件(包),每个组件(包)支持不同的方面是很正常的。如果你已经学会了Ada,那么首先编写软件包的规范,争论(也许是和你自己)为什么这是正确的设计,然后实现它们是很正常的。我认为,这在任何支持规范和主体的语言中都是正常的——例如,C

就我个人而言,我会边走边检查汇编,以确保我没有做任何愚蠢的事情

至于分离-一个(不是很好)的原因是减少混乱,以防止单位变得太长。另一个原因(对于我编写的代码生成器)是代码生成器不需要担心在UML模型中保留开发人员手工编写的代码;所有代码体都是分开的。第三种可能是与环境相关的实现(例如,Windows vs Unix),您可以让编译器看到每个环境的不同版本的单独主体(不过,人们通常使用库包)

编译器对文件名和编译顺序有自己的规则。当蚊虫看到

procedure Foo is
   procedure Bar is separate;
它希望在名为
Foo.adb
的文件中找到Foo的身体,在
Foo-Bar.adb
中找到Bar的身体(我相信,你可以告诉它不同的-
gnatmake
的包
命名
,但可能不值得麻烦)。这里最好顺其自然

separate (Foo)
procedure Bar is
很清楚

您可以编译
foo bar.adb
,这将进行完整的分析并捕获代码中几乎所有的错误;但是GNAT无法自行生成代码。相反,当您编译
foo.adb
时,它会在一个生成的对象文件中包含所有单独的实体。这样做当然没有错


使用GNAT,无需担心编译顺序,您可以按照自己喜欢的任何顺序编译。但最好使用gnatmake,让计算机承受压力

您确实可以按照您描述的方式工作,当然,除非在所有包体都有某种实现之前,您的程序不会链接。出于这个原因,我认为这更重要
begin
   null;
end;
begin
   return The_Return_Type'first; --'
end;