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