我如何才能使该调度呼叫正常工作?

时间:2019-05-19 23:52:05

标签: class oop ada tag-dispatching

我正在尝试使自己熟悉Ada中的面向对象。几个月前,您的网站帮助我解决了另一个O-O问题,希望您愿意再次提供帮助。

情况:我有一个抽象类型“ token”和2个派生类型“ otoken”和“ vtoken”。我想将2个派生类型放在同一数组中,并让它们正确调度。

我的教科书建议将数组声明为包含指向令牌类的指针,这使我不得不遍历所有点。下面是我程序的精简版本,但由于编译器说我的调度调用“含糊不清”,因此无法编译

---------------------------------------------------------------------------------

--------------------------------------------
-- Tokensamp.ads
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Command_Line; use Ada.Command_Line;
package tokensamp is
    type token is abstract tagged record
    x: integer;
    end record;
    type otoken is new token with record
    y: integer;
    end record;
    type vtoken is new token with record
    z: integer;
    end record;

    type potoken is access otoken;
    type pvtoken is access vtoken;

end tokensamp;
------------------------------------------------------------------------------------------------------
-- Parsesamp.ads:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Command_Line; use Ada.Command_Line;
with tokensamp; 
package parsesamp is
    function rootOfTree( t: tokensamp.pvtoken) return integer;
    function rootOfTree( t: tokensamp.potoken) return integer;  
end parsesamp; 
-------------------------------------------
-- parsesamp.adb:
package body parsesamp is 
    function rootOfTree( t: tokensamp.pvtoken) return integer  is
    begin
       return   t.z * 2;
    end rootOfTree;

    function rootOfTree( t: tokensamp.potoken) return integer is
    begin
        return  t.y * 2;
    end rootOfTree;
    result: integer;
    type tarray is array (1..2) of access tokensamp.token'class ;
    tl: tarray;
begin
    for i in 1..2 loop
    result := rootOfTree(  tl(i) );
    end loop;

end parsesamp;
-------------------------------------------------------------

当我使用GNAT Ada 95编译器进行编译时,收到错误消息:

C:\GNAT\2018\bin\ceblang>gnatmake   parsesamp.adb
gcc -c parsesamp.adb
parsesamp.adb:25:27: ambiguous expression (cannot resolve "rootOfTree")
parsesamp.adb:25:27: possible interpretation at parsesamp.ads:9
parsesamp.adb:25:27: possible interpretation at parsesamp.ads:8
gnatmake: "parsesamp.adb" compilation error

换句话说,它无法将这两个功能识别为备用调度呼叫。如果您能提出建议,我将不胜感激,因为我已经坚持了好几天。

3 个答案:

答案 0 :(得分:1)

您的困惑似乎包括使用软件包和在Ada中定义分派操作的方式。 必须在定义标记数据类型的同一包中定义调度操作,但必须先定义其他类型。

mean_function<-function(x){
mean(x)
}

程序包规范定义了标记的类型Token及其调度操作Root_Of_Tree。记录类型令牌包含一个名为X的整数数据元素。 包的主体是:

testset "number with 1-5" <-raw[number"number 1-5",]

我已使用子程序包定义Otoken和Vtoken类型。

package Tokens is
   type token is tagged private;
   function Root_Of_Tree(T : Token) return Integer;
   type Token_Access is access all Token'Class;
   type Token_Array is array (Positive range <>) of Token_Access;
private
   type Token is tagged record
      X : Integer := 1;
   end record;
end Tokens;

Tokens.OTokens的主体为:

    package body Tokens is

       ------------------
       -- Root_Of_Tree --
       ------------------

       function Root_Of_Tree (T : Token) return Integer is
       begin
          return T.X;
       end Root_Of_Tree;

    end Tokens;

令牌规范。VToken是:

package Tokens.OTokens is
   type Otoken is new Token with private;
   function Root_Of_Tree(T : Otoken) return Integer;
private
   type Otoken is new Token with record
      Y : Integer := 2;
   end record;

end Tokens.OTokens;

主体Token.Vtokens为:

package body Tokens.OTokens is

   ------------------
   -- Root_Of_Tree --
   ------------------

   function Root_Of_Tree (T : Otoken) return Integer is
   begin
      return T.Y * 2;
   end Root_Of_Tree;

end Tokens.OTokens;

创建包含一个otoken和一个vtoken的数组的主要过程是:

package tokens.vtokens is
   type vtoken is new token with private;
   function Root_Of_Tree(T : vtoken) return Integer;
private
   type vtoken is new token with record
      Z : Integer := 3;
   end record;

end tokens.vtokens;

请记住,OToken类型包含两个字段X和Y。VToken类型包含两个字段X和Z。 主要过程的输出为:

package body tokens.vtokens is

   ------------------
   -- Root_Of_Tree --
   ------------------

   function Root_Of_Tree (T : vtoken) return Integer is
   begin
      return T.Z * 2;
   end Root_Of_Tree;

end tokens.vtokens;

答案 1 :(得分:1)

首先,您需要将rootOfTree声明为抽象操作 之token

type token is abstract tagged record
   x: integer;
end record;
function rootOfTree( t: tokensamp.token) return Integer is abstract;

(必须在token之前声明原始操作 冻结,基本上是在声明使用之前 派生类型)。

然后声明otokenvtoken的原始操作;他们 必须在与它们相应类型相同的包中声明为 是原始的,即可以分派给。

type otoken is new token with record
   y: integer;
end record;

type vtoken is new token with record
   z: integer;
end record;

function rootOfTree( t: tokensamp.vtoken) return integer;
function rootOfTree( t: tokensamp.otoken) return integer;

(在每个参数之后立即声明它们会更正常 类型,但由于两者都不冻结,所以可以)。

请注意,rootOfTree操作均不采用访问类型 参数。

您可能不需要考虑potokenpvtoken 在此处声明类级指针:

type ptoken is access token'class;

然后,您需要为package tokensamp声明一个正文,其中包含 这两个具体rootOfTree的实现。

考虑到parsesamp,您既不能在这里声明rootOfTree

你可以写

result := tokensamp.rootOfTree (t1(i).all);

({t1(i)是指向类范围的指针,.all是类范围的值,并且 tokensamp.rootOfTree是可调度的操作,因此这是一个 调度电话)

..或者您可以使用更漂亮的速记

result := t1(i).rootOfTree;

答案 2 :(得分:0)

作为吉姆·罗杰斯(Jim Rogers)和西蒙·赖特(Simon Wright)给出的答案的补充,如果您要使用Ada 2012,则可以考虑使用不定的持有人来构造数组(另请参见RM A.18.18Ada 2012 Rationale, section 8.5

如基本原理所述,持有人是可以容纳(和管理)单个对象实例的容器。将对象作为参数传递给To_Holder子程序时(请参见下面的示例),将其复制到堆实例中,然后在不再需要该对象时将其销毁(例如,当替换它或持有人出局时)范围)。因此,就像直接使用access类型时那样,持有人容器使您不必手动管理内存。

(性能)成本是复制传递到To_Holder程序的对象。您可以在保持器之间“移动”对象(使用在保持器包中定义的Move子程序),但是不能将对象“移动”到保持器中。您只能将其复制到支架中。

token.ads (规范)

package Tokens is

   --  Abstract Root Type.

   type Token is abstract tagged private;   
   function Root_Of_Tree (T : Token) return Integer is abstract;

   --  First derived type.

   type OToken is new Token with private;         
   function Root_Of_Tree (T : OToken) return Integer;   
   function Create_OToken (X, Y : Integer) return OToken;

   --  Second derived type.

   type VToken is new Token with private;   
   function Root_Of_Tree (T : VToken) return Integer;   
   function Create_VToken (X, Z : Integer) return VToken;

private

   type Token is abstract tagged record
      X : Integer;
   end record;

   type OToken is new Token with record
      Y : Integer;
   end record;

   type VToken is new Token with record
      Z : Integer;
   end record;

end Tokens;

tokens.adb (正文)

package body Tokens is   

   function Root_Of_Tree (T : OToken) return Integer is
   begin
      return T.X + 2 * T.Y;
   end Root_Of_Tree;   

   function Create_OToken (X, Y : Integer) return OToken is
   begin
      return OToken'(X, Y);
   end Create_OToken;   

   function Root_Of_Tree (T : VToken) return Integer is
   begin
      return T.X + 3 * T.Z;
   end Root_Of_Tree;

   function Create_VToken (X, Z : Integer) return VToken is
   begin
      return VToken'(X, Z);
   end Create_VToken;

end Tokens;

main.adb

with Ada.Text_IO; use Ada.Text_IO;
with Tokens;      use Tokens;

with Ada.Containers.Indefinite_Holders;

procedure Main is

   package Token_Holder is
     new Ada.Containers.Indefinite_Holders (Token'Class);
   use Token_Holder;

   type Token_Array is array (Integer range <>) of Holder;


   Tokens : Token_Array :=
     (To_Holder (Create_OToken (1, 2)),
      To_Holder (Create_OToken (5, 4)),
      To_Holder (Create_VToken (1, 2)),
      To_Holder (Create_VToken (5, 4)));

begin

   for T of Tokens loop
      Put_Line (Integer'Image (T.Element.Root_Of_Tree));
   end loop;

end Main;

运行valgrind表示程序终止时没有剩余的内存分配:

$ valgrind ./main
==1392== Memcheck, a memory error detector
==1392== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==1392== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==1392== Command: ./main
==1392== 
 5
 13
 7
 17
==1392== 
==1392== HEAP SUMMARY:
==1392==     in use at exit: 0 bytes in 0 blocks
==1392==   total heap usage: 8 allocs, 8 frees, 160 bytes allocated
==1392== 
==1392== All heap blocks were freed -- no leaks are possible
==1392== 
==1392== For counts of detected and suppressed errors, rerun with: -v
==1392== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

注意[已更新]:有8个分配,而数组仅包含4个元素/持有者。这是因为如何为support atomic increment/decrements的平台(例如Linux)实现持有者。对于这些平台,实现在内部创建另一个“共享持有人”,以支持写时复制策略(请参见source)。对于不支持原子增量/减量的平台,实现会更简单(请参见source),并且只会显示4种分配。