【问题标题】:Is there a way to create and reuse aspect sets in Ada (specifically GNAT)?有没有办法在 Ada(特别是 GNAT)中创建和重用方面集?
【发布时间】:2020-09-12 16:21:02
【问题描述】:

在 C/C++ 中,我经常使用预处理器来定义基于通用基本过程(是的,我知道,C 中的函数)的过程。

例如(名称、类型和值是假设的):

// in some .h file
void some_base_procedure(int a, char c);

#define proc1(a) some_base_procedure(a, 'd')
#define proc2(a) some_base_procedure(a, 'e')
#define proc3(a) some_base_procedure(a, 'f')

我查看了 Ada 中的泛型并将它们用于包,但对于子程序,我不确定如何干净地执行类似于上述 C 示例的操作。

我确实想出了这个:

-- in some .ads file
procedure some_base(a:integer; c: character);

procedure proc1(a:integer; b: character := 'd') with
  Import    => True,
  Address   => some_base'Address;
procedure proc2(a:integer; b: character := 'e') with
  Import    => True,
  Address   => some_base'Address;
procedure proc3(a:integer; b: character := 'f') with
  Import    => True,
  Address   => some_base'Address;

这实际上工作得很好,我只需要在相关的 .adb 文件中为 some_base 实现一个主体,我不必实现只需使用正确参数值调用 some_base 的 proc1、proc2、proc3 子程序主体。在我的一些用例中,虽然我有更多方面,但只有 Import 和 Address,因此可能无法很好地扩展。

由于没有更好的术语,我将它们称为参数化子程序别名。

上述方法的一些问题:

  1. 仍然可以在调用 proc1、proc2 或 proc3 时覆盖 b。这是次要的,因为必须故意这样做(以及出于什么目的?)
  2. 它可能被认为通常不是 Ada 的做事方式(可能被认为是 C'ism),并且可能有更简洁的方式来使用泛型,但如果它涉及每个子程序的单独 ads/adb,那么对于上述这样一个简单的用例来说就太冗长了。
  3. 如果每个参数化子程序别名的方面变成几行,那么现在在维护期间需要更新更多,并且成为类似于需要每个参数化子程序别名的显式主体的维护问题。

所以我的问题是关于上述最后一点。

有没有办法将Import => True, Address => some_base'Address; 放入某种方面集,然后为每个参数化子程序别名重用它?

所以它会是这样的(aspect_set,using some_base_set,...弥补了这个例子):

-- in some .ads file
procedure some_base(a:integer; c: character);
aspect_set some_base_set is Import    => True, Address   => some_base'Address;

procedure proc1(a:integer; b: character := 'd') using some_base_set;
procedure proc2(a:integer; b: character := 'e') using some_base_set;
procedure proc3(a:integer; b: character := 'f') using some_base_set;

即使没有,我认为我的上述方法已经足够好,除非有人令人信服地指出为什么它是一种非常糟糕的方法,并且有一种更具表现力的 类似 Ada 的方法 做这样的事情。

【问题讨论】:

  • 我不太清楚为什么以这种方式覆盖子程序让我不寒而栗!
  • 我不确定这是便携的。使用添加或更改的参数默认值进行重命名(您在下面发现)是 Ada Way (TM)。

标签: generics ada gnat


【解决方案1】:

在研究了更多之后,我找到了一个更通用的解决方案。

-- in some .ads file
procedure some_base(a:integer; c: character);
procedure proc1(a:integer; b: character := 'd') renames some_base;
procedure proc2(a:integer; b: character := 'e') renames some_base;
procedure proc3(a:integer; b: character := 'f') renames some_base;

这比我使用的更干净,即使仍然可以覆盖默认值,但那是次要的。它解决了维护问题,这首先是问题的重点。

【讨论】:

    【解决方案2】:

    您在使用泛型时遇到了什么问题?

    generic
       C : Character;
    procedure Some_Base(A : Integer);
    
    procedure Some_Base(A : Integer) is
    begin
       -- do something with A and B;
    end Some_Base;
    
    
    procedure Proc_1 is new Some_Base('d');
    procedure Proc_2 is new Some_Base('e');
    procedure Proc_3 is new Some_Base('f');
    

    【讨论】:

    • 根据您的回答,我刚刚尝试了类似的方法。在 .adb 文件中,也许我可以做到这一点?但是我不能将 some_base 的主体放在 .adb 文件中,即 .ads 文件中的通用原型中,而不会收到警告:在看到主体之前无法实例化“Some_Base”。我希望在同一个 .ads 文件中定义所有派生过程。
    • Some_Base 可以在自己的 .ads 和 .adb 中
    • 我可以像你说的那样让泛型工作,我只是没有让它们与我的用例一起工作,其中最终派生函数正在设置其他默认值(例如在使用 GNAT.Source_Info 时,它适用于“重命名”方法。
    • 好吧,只需要使用 Some_Base 就可以了……无需重命名、实例化或导入
    • 我搞定了。使用泛型函数给包层次结构增加了一些复杂性,但这很好。在我刚刚拥有 package1.ads 和 package1.adb 之前。现在我有 package1.ads、package2.ads、base_procedure.ads、base_procedure.adb。 base_procedure 使用 package1,package2 使用 base_procedure。在这方面有点奇怪,但我得到了它的工作。
    猜你喜欢
    • 1970-01-01
    • 2017-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-24
    • 2015-03-26
    相关资源
    最近更新 更多