在没有优化的情况下编译以下程序,然后运行它,我看到 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 回答
如果
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中,我也会避免这样做 . 可能会有一个更面向对象的问题解决方案(请注意,您可以使用标记类型来进行间接子程序调用,因为标记类型操作正在调度) . 例如 .在所有情况下可能不是最好的设计,但是如果需要检查特定子程序的子程序地址,则应考虑这样的事情 . 一方面,这更灵活;程序员可以定义另一个派生类型,其中
Apply
总是返回False
但执行其他操作,例如将参数写入日志文件 .我认为它必须依赖于您的操作系统和编译器 . 在Mac OS X上使用FSF GCC 5.1.0,您的代码不会引发异常 .
也就是说,我认为避免
.all’Address
更为自然(Ada 95杰出评论家之一告诉我,当他真正需要的是一种适当的类型转换时,他已经习惯说.all’Access
) . 代码的这种扩展不会引发异常案件 .(后来)
我重写了这个不使用异常......
在Mac OS X上使用GNAT GPL 2014,这就给出了