首页 文章

什么是作为访问参数传递的Ada子程序的地址?

提问于
浏览
0

在没有优化的情况下编译以下程序,然后运行它,我看到 Program_Error

raised PROGRAM_ERROR : addreq.adb:16 explicit raise

或者,鉴于Simon Wright的回答, updating

raised PROGRAM_ERROR : using address

这种情况发生在Mac OS X或GNU / Linux x84_64上,在Linux上使用GNAT GPL 2014时,奇怪的是,仅适用于我的程序 . 其他版本的GNAT产生的代码并不令我感到惊讶 . 由于我的程序需要识别子程序的地址,我希望在RM中有明确的陈述; 13.3 (11) has ramifications that grow waivers, IINM . 所以,参考下面的程序,会

Yes_no.all'Address = No'Adress

如果由LRM解释,是一个真实的陈述?合法的? Yes_No.all 实际上是一个引用函数 No (如果选择 'Address )的正确方法吗?由于存在通过不同指针类型的间接,具有更深入可访问性的指针类型,这是否会改变图片?我认为 Yes_No.all 应该与 No 相同 'Address ,但显然不是,有些编译器 .

with System;

procedure Addreq is

   function No (Ignored : Integer) return Boolean is
   begin
      return False;
   end No;

   procedure Taking
     (Yes_No : access function (N : Integer) return Boolean)
   is
      use type System.Address;
   begin
      if Yes_No.all'Address /= No'Address then
         raise Program_Error;
      end if;
   end Taking;

begin
   Taking (No'Access);
end Addreq;

还有一个更新:如果我将 Addreq 打包并让另一个子程序调用 Taking ,那么

with Addreq;  --  now a package
procedure Driver is
   use Addreq;
begin
   Taking (No'Access);
end Driver;

然后没有异常被提出 .

2 回答

  • 1

    如果 Yes_No.all'Address 不等于 No'Address ,则很可能 Yes_No.all'Address 是某种包装器代码的地址 .

    No 是嵌套在过程中的函数 . 如果你说 No'access ,编译器通常不能简单地创建一个单字指针,其值是 No 的地址 . 原因是当代码通过访问值进行间接调用时,代码必须执行一些特殊操作,以便 No 可以访问属于 addreq 的局部变量,这些变量将位于堆栈的某个位置 . 例如,提供此访问权限的一种方法是将静态链接作为参数传递给 No ;这是一个额外的指针,指向 addreq 的堆栈帧,它将包含其局部变量(或沿着这些行的某些东西) . 因此,当通过访问进行间接调用时,调用者必须知道静态链接是什么 . 一种解决方案是制作嵌套的访问功能类型的dope向量,其中包含函数地址和静态链接 . 另一个是生成包装器代码 . 包装器代码负责使用静态链接参数调用被调用的子程序,然后访问值只是一个单字指针,它是包装器代码的地址 . 我相信GNAT采用这种方法 . 优点是它可以将 My_Function'access 作为参数传递给C函数,以用作回调 . 当C代码通过函数指针调用时,它调用包装函数,然后使用正确的静态链接调用嵌套函数 . 有大量的公共Ada代码依赖于这种机制 . (GtkAda严重依赖它 . )

    但是,如果访问值指向包装器而不是实际函数,则 The_Access.all'Address 将不会返回您认为应该的内容 . 当代码执行 The_Access.all'Address 时,如果 The_Access 是一个带有地址的单个字,那么所有属性都可以返回 - 指针中的地址 .

    More: 我不知道原始代码是一个更大的例子的一部分,还是只是一个测试来看看编译器的作用 . 但是比较 'Address 值以查看子程序访问参数是否指向特定的子程序会让我觉得设计不佳,并且比较 'Access 并不是更好 . 即使在C中,我也会避免这样做 . 可能会有一个更面向对象的问题解决方案(请注意,您可以使用标记类型来进行间接子程序调用,因为标记类型操作正在调度) . 例如 .

    type Boolean_Function_Object is abstract tagged null record;
    function Apply (Obj : Boolean_Function_Object; N : Integer) return boolean;
    function Is_Constant_False (Obj : Boolean_Function_Object) return boolean;
    
    type No_Function is new Boolean_Function_Object with null record;
    overriding
    function Apply (Obj : No_Function; N : Integer) return boolean is (False);
    overriding
    function Is_Constant_False (Obj : No_Function) return boolean is (True);
    
    procedure Taking (Func : Boolean_Function_Object) is
    begin
        if not Func.Is_Constant_False then
            raise Program_Error;
        end if;
    end Taking;
    

    在所有情况下可能不是最好的设计,但是如果需要检查特定子程序的子程序地址,则应考虑这样的事情 . 一方面,这更灵活;程序员可以定义另一个派生类型,其中 Apply 总是返回 False 但执行其他操作,例如将参数写入日志文件 .

  • 3

    我认为它必须依赖于您的操作系统和编译器 . 在Mac OS X上使用FSF GCC 5.1.0,您的代码不会引发异常 .

    也就是说,我认为避免 .all’Address 更为自然(Ada 95杰出评论家之一告诉我,当他真正需要的是一种适当的类型转换时,他已经习惯说 .all’Access ) . 代码的这种扩展不会引发异常案件 .

    with Ada.Text_IO; use Ada.Text_IO;
    with System;
    
    procedure Addreq is
    
       function No (Ignored : Integer) return Boolean is
       begin
          return False;
       end No;
    
       procedure Taking
         (Yes_No : access function (N : Integer) return Boolean)
       is
          use type System.Address;
       begin
          if Yes_No.all'Address /= No'Address then
             raise Program_Error with "using address";
          end if;
          Put_Line ("using address was OK");
          if Yes_No /= No'Access then
             raise Program_Error with "using access";
          end if;
          Put_Line ("using access was OK");
       end Taking;
    
    begin
       Taking (No'Access);
    end Addreq;
    

    (后来)
    我重写了这个不使用异常......

    with Ada.Text_IO; use Ada.Text_IO;
    with System;
    
    procedure Addreq is
    
       function No (Ignored : Integer) return Boolean is
       begin
          return False;
       end No;
    
       procedure Taking
         (Yes_No : access function (N : Integer) return Boolean)
       is
          use type System.Address;
       begin
          Put_Line
            ((if Yes_No.all'Address /= No'Address
              then "using address failed"
              else "using address was OK"));
          Put_Line
            ((if Yes_No /= No'Access
              then "using access failed"
              else "using access was OK"));
       end Taking;
    
    begin
       Taking (No'Access);
    end Addreq;
    

    在Mac OS X上使用GNAT GPL 2014,这就给出了

    $ ./addreq 
    using address failed
    using access was OK
    

相关问题