【问题标题】:(Ada 2012) compile-time error "expected private type... found composite type"(Ada 2012)编译时错误“预期私有类型...找到复合类型”
【发布时间】:2019-08-31 20:41:16
【问题描述】:

我正在尝试在 Ada 2012 中编写一个非常原始的链表示例程序。我的代码由 3 个文件组成,linked_list.adblinked_list.adsmain.adb

用户将运行程序并简单地输入一个数字序列,然后输入零来结束序列并退出。程序只是从标准输入中读取这些数字,打印出列表然后退出。

这是我的完整代码...

文件:“main.adb”

with Linked_List; use Linked_List;

procedure Main is
  L : access List_Item;
begin
  L := new List_Item'(null, 0);

  while Append_Item (L) loop
    null;
  end loop;

  Print_List (L);
end Main;

文件:“linked_list.ads”

with Ada.Text_IO; use Ada.Text_IO;

package Linked_List is
  type List_Item is private;

  function Append_Item (List_Head : access List_Item) return Boolean;
  procedure Print_List (List_Head : access List_Item);

private

  type List_Item is
    record
      Next_Item : access List_Item;
      ID : Integer;
    end record;

end Linked_List;

文件:“linked_list.ads”

with Ada.Text_IO; use Ada.Text_IO;

package body Linked_List is

  function Append_Item (List_Head : access List_Item) return Boolean is
    Runner : access List_Item := List_Head;
    new_ID : Integer;
  begin
    if Runner.Next_Item = null then -- if we've found the last item
      Put ("Enter ID for new Item (enter 0 to stop): ");
      Get (new_ID);

      if new_ID = 0 then
        return false; -- user wants to quit
      else if;
        -- add a new item to the end of the list
        Runner.Next_Item := new List_Item'(null, new_ID);
        return true;
      end if;
    else;
      Runner := Runner.Next_Item;
    end if;
  end Append_Item;

  procedure Print_List (List_Head : access List_Item);
    Runner : access List_Item := List_Head;
  begin
    if Runner = null then
      return;
    else;
      Put ("Item ID: "); Put (Runner.ID);
      Runner := Runner.Next_Item;
    end if;
  end Print_List;

end Linked_List;

我使用的是 Gnatmake 7.4.0,我的编译器命令行是

gnatmake -gnaty -gnaty2 -gnat12 main.adb

我看到的错误信息是:

gnatmake -gnaty -gnaty2 -gnat12 main.adb
aarch64-linux-gnu-gcc-7 -c -gnaty -gnaty2 -gnat12 main.adb
main.adb:6:22: expected private type "List_Item" defined at linked_list.ads:4
main.adb:6:22: found a composite type
gnatmake: "main.adb" compilation error
Makefile:2: recipe for target 'all' failed
make: *** [all] Error 4

我编写的语法似乎与我试图学习的书一致:John Barnes 的“Programming in Ada 2012”。

该记录是私下声明的,因此我的客户端程序(主程序)看不到列表机制内部工作的血腥细节。我做错了什么?

【问题讨论】:

  • 在我看来new List_Item'(null, 0);正是利用了列表机制的血腥细节!
  • 吹毛求疵 - 列表和列表项是完全不同的东西,最好不要给它们同名

标签: ada


【解决方案1】:

原因是:type List_Item is private(来自 Ada 代码的简单英语)!

这意味着包 Linked_List 的作者不希望它的用户使用它的详细信息(它是一个包含两个组件的记录)。在更复杂的软件中,隐藏这些细节很有用,因为它们可能会发生变化,如果用户使用在 List_Item 类型的设计更改后变得不兼容的细节(在本例中为复合类型),则会遇到麻烦. 有两种解决方案:

  1. 将 List_Item 公开(如果您想构建“真正的”软件,这通常是一个糟糕的解决方案)
  2. type List_Item is privateNull_List : constant List_Item := (null, 0); 后添加Null_List : constant List_Item; 在私密部分。然后你可以在你的 main.adb 中使用Null_List

【讨论】:

  • 所以问题出在'(null, 0) 部分?我认为“私人”的这种用法让我感到困惑。私有的不是类型,而是内容。我想我现在明白了。
  • 你可以让一个类型的名字对公众不可见,方法是只在私有部分(删除“类型 T 是私有的”),或者在正文中定义它。
  • @Wossname 实际上类型是私有的。问题是您试图通过公开使用其组件来初始化私有类型(当您尝试 '(null,0) 时会发生这种情况)。由于类型是私有的,因此不允许编译器知道如何使用您提供的那些组件来构造对象。
  • @Zerte 要添加到列表中的另一个选项:创建一个构造函数:函数 Make_Default_List 返回 List_Item。与常量没有太大区别,但如果您想进行一些复杂的初始化并且常量太受限而无法执行,则很有用。
  • 我最终确实创建了一个“Create_Empty_List”函数。我猜想 Ada 中“private”的用法与 C# 中的用法略有不同。在 C# 中,如果您将某些内容声明为私有,您甚至无法从调用范围内看到它存在,但在 Ada 中看起来这意味着它是可见的,但您无法检查它的工作原理。这是一个奇怪的概念,但我想这只是习惯的东西。
【解决方案2】:

确实该类型是私有的,这意味着客户端“main.adb”不能像在“new”调用中那样对其内部进行假设,并且 Zerte 的两种解决方案都可以解决这个问题(我同意:首先不是解决方案!)

第三种解决方案:“对象工厂”设计模式。

package Linked_List is
  type List_Item is private;

  function Create_Item(ID_Value : Natural) return List_Item;

函数体应该很明显。请注意,对于更复杂的示例,尤其是 limited private 类型,可能需要 Ada-2005 的“扩展返回”语法。

可以使用不同类型项目的变体重载此构造函数(实际上是对象工厂的示例),并提供 Zerte 的 Null_List : constant List_Item; 作为替代方案。

客户端可以使用这个功能

L := Create_Item(0);

【讨论】:

    猜你喜欢
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-09
    • 2019-03-27
    • 2019-10-30
    • 1970-01-01
    相关资源
    最近更新 更多