Post 条件和限制 in out 参数

Post conditions and limited in out parameters

post条件如何指定limited in out参数在子程序调用时不被修改?考虑以下代码:

with Ada.Text_IO;

procedure Main is

   package C is

      type Printer is tagged limited private;

      procedure Print
        (P : in out Printer;
         B : Integer);

   private

      type Printer is tagged limited record
         A : Integer := 0;
      end record;

   end C;

   package body C is

      procedure Print
        (P : in out Printer;
         B : Integer) is
      begin
         if B >= 0 then
            P.A := B;
         end if;
         Ada.Text_IO.Put_Line (P.A'Image);
      end Print;

   end C;

   P : C.Printer;
begin
   P.Print (-1);
end Main;

当指定负数作为 Print 子程序的输入时,Printer 实例的内部状态在子程序调用期间保持不变。指定它的最佳方法是什么?尝试以下操作会产生错误消息 "attribute 'Old' cannot apply to limited objects":

procedure Print
    (P : in out Printer;
     B : Integer) with
    Post => (if B < 0 then P'Old = P);

能否在 Ada 2020 标准的增量聚合中找到解决方案?

编辑:考虑到有限类型根据定义没有任何相等运算符,这可能是一个棘手的问题。

来自 RM 6.1.1

For each X'Old in a postcondition expression that is enabled, a constant is implicitly declared at the beginning of the subprogram or entry. The constant is of the type of X and is initialized to the result of evaluating X (as an expression) at the point of the constant declaration. The value of X'Old in the postcondition expression is the value of this constant; the type of X'Old is the type of X. These implicit constant declarations occur in an arbitrary order.

AARM 非常明确:

'Old makes no sense for limited types, because its implementation involves copying. It might make semantic sense to allow build-in-place, but it's not worth the trouble.

我认为增量聚合不会解决问题,因为我看不出它如何防止隐式复制。

然而,虽然您不能将 'old 应用于受限对象,但您可以将其应用于其一个或多个非受限组件。

  procedure Print
    (P : in out Printer;
     B : Integer) with Post => (if B < 0 then P.A'Old = P.A);

当然,在您的示例中,组件 A 是私有的,因此这将不起作用,并且通常不适合 public 过程。

要解决这个问题,您可能需要将组件包装在 getter 函数中并编写:

      function Printer_Value (P : in Printer) return Integer;

      procedure Print
        (P : in out Printer;
         B : Integer) with Post => (if B < 0 then P.Printer_Value'Old = P.Printer_Value);

   private

      function Printer_Value (P : in Printer) return Integer is (P.A);

然后调整 Printer_Value 以手动包含应检查不变性的组件。但这也将无法编译,因为 P.Printer_Value'Old 可能未评估(当 B>=0 时)。 AARM 声明通常的解决方案是 P'Old.Printer_Value 但这是不可能的,因为 P 是有限的。所以要让它工作,我们必须无条件地评估它:

  procedure Print
    (P : in out Printer;
     B : Integer) with Post => P.Printer_Value'Old = P.Printer_Value or else B >= 0;

这几乎达到了您想要的效果,但由于维护负担,我怀疑它是否值得。