作为访问参数传递的 Ada 子程序的地址是什么?
What is the 'Address of an Ada subprogram passed as an access parameter?
编译下面的程序没有优化,然后运行它,我看到Program_Error
,
raised PROGRAM_ERROR : addreq.adb:16 explicit raise
或者,正在更新 鉴于 Simon Wright 的回答,
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。所以,参考下面的程序,would
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;
那么不会引发异常。
我想这一定取决于你的 OS 和编译器。在 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
如果Yes_No.all'Address
不等于No'Address
,那么很可能Yes_No.all'Address
是某种包装代码的地址。
No
是嵌套在过程中的函数。如果你说No'access
,编译器一般不能简单地创建一个值为No
的地址的单字指针。原因是当代码通过访问值进行间接调用时,代码必须做一些特殊的事情,以便 No
可以访问属于 addreq
的局部变量,这些局部变量将位于堆栈的某个位置。例如,提供此访问的一种方法是将 static link 作为参数传递给 No
;这是一个额外的指针,指向 addreq
的栈帧,其中将包含它的局部变量(或类似的东西)。因此,当通过访问进行间接调用时,调用者必须知道静态 link 是什么。一种解决方案是制作嵌套的函数访问类型掺杂向量,其中包含函数地址和静态 link。另一种是生成包装代码。包装代码负责调用带有静态link参数的被调用子程序,访问值则只是一个单字指针,即包装代码的地址。我相信 GNAT 采用这种方法。优点是它可以将 My_Function'access
作为参数传递给 C 函数,用作回调。当 C 代码通过函数指针调用时,它会调用包装函数,该函数然后使用正确的静态 link 调用嵌套函数。有大量 public Ada 代码依赖于此机制。 (GtkAda 严重依赖它。)
但是,如果访问值指向一个包装器,而不是实际的函数,那么 The_Access.all'Address
将不会 return 您认为应该的那样。当代码执行The_Access.all'Address
时,如果The_Access
是一个单独的单词,里面有一个地址,那么属性就可以了return——指针中的地址。
更多: 我不知道原始代码是一个更大示例的一部分,还是只是一个测试,看看编译器做了什么。但是比较 '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
始终 return False
但会做其他事情,例如将参数写入日志文件。
编译下面的程序没有优化,然后运行它,我看到Program_Error
,
raised PROGRAM_ERROR : addreq.adb:16 explicit raise
或者,正在更新 鉴于 Simon Wright 的回答,
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。所以,参考下面的程序,would
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;
那么不会引发异常。
我想这一定取决于你的 OS 和编译器。在 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
如果Yes_No.all'Address
不等于No'Address
,那么很可能Yes_No.all'Address
是某种包装代码的地址。
No
是嵌套在过程中的函数。如果你说No'access
,编译器一般不能简单地创建一个值为No
的地址的单字指针。原因是当代码通过访问值进行间接调用时,代码必须做一些特殊的事情,以便 No
可以访问属于 addreq
的局部变量,这些局部变量将位于堆栈的某个位置。例如,提供此访问的一种方法是将 static link 作为参数传递给 No
;这是一个额外的指针,指向 addreq
的栈帧,其中将包含它的局部变量(或类似的东西)。因此,当通过访问进行间接调用时,调用者必须知道静态 link 是什么。一种解决方案是制作嵌套的函数访问类型掺杂向量,其中包含函数地址和静态 link。另一种是生成包装代码。包装代码负责调用带有静态link参数的被调用子程序,访问值则只是一个单字指针,即包装代码的地址。我相信 GNAT 采用这种方法。优点是它可以将 My_Function'access
作为参数传递给 C 函数,用作回调。当 C 代码通过函数指针调用时,它会调用包装函数,该函数然后使用正确的静态 link 调用嵌套函数。有大量 public Ada 代码依赖于此机制。 (GtkAda 严重依赖它。)
但是,如果访问值指向一个包装器,而不是实际的函数,那么 The_Access.all'Address
将不会 return 您认为应该的那样。当代码执行The_Access.all'Address
时,如果The_Access
是一个单独的单词,里面有一个地址,那么属性就可以了return——指针中的地址。
更多: 我不知道原始代码是一个更大示例的一部分,还是只是一个测试,看看编译器做了什么。但是比较 '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
始终 return False
但会做其他事情,例如将参数写入日志文件。