我与Ada一起迈出了第一步,而且我发现我很难理解如何做其他语言即时的常见甚至平庸的操作。
在这种情况下,我定义了以下任务类型(和访问类型,以便我可以创建新实例):
task type Passenger(
Name : String_Ref;
Workplace_Station : String_Ref;
Home_Station : String_Ref
);
type Passenger_Ref is access all Passenger;
正如您所看到的,它是一个简单的任务,在创建实例时有3个可以传递给它的判别式。 String_Ref
定义为:
type String_Ref is access all String;
我使用它,因为显然你不能使用"正常"类型作为任务判别式,仅作为引用或原始类型。
所以我想创建一个这样的任务的实例,但无论我做什么,我都会收到错误。我不能直接传递字符串:
Passenger1 := new Passenger(Name => "foo", Workplace_Station => "man", Home_Station => "bar");
因为那些是字符串而不是对字符串的引用,所以很公平。 所以我试过了:
task body Some_Task_That_Tries_To_Use_Passenger is
Passenger1 : Passenger_Ref;
Name1 : aliased String := "Foo";
Home1 : aliased String := "Man";
Work1 : aliased String := "Bar";
begin
Passenger1 := new Passenger(Name => Name1'Access, Workplace_Station => Work1'Access, Home_Station => Home1'Access);
但这也不起作用,因为根据我的理解,Home1
/ Name1
/ Work1
变量是任务Some_Task_That_Tries_To_Use_Passenger
的本地变量,因此Passenger"构造函数"
说实话,我不明白我该怎么做。我过去曾使用过几种编程语言,但是我从来没有遇到过将一个简单的String传递给构造函数的麻烦,我觉得这完全是白痴,但我不明白为什么这样的常见操作会如此很复杂,我确定我没有错误地处理这个问题,请赐教并告诉我正确的方法,因为我疯了:D
答案 0 :(得分:3)
是的,我同意这是一个严重的语言问题,区分任务和记录类型必须是离散的。幸运的是,有一个简单的任务类型解决方案 - 数据可以通过"条目"点。
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure Main is
task type Task_Passenger is
entry Construct(Name, Workplace, Home : in String);
end Passenger;
task body Task_Passenger is
N, W, H : Unbounded_String;
begin
accept Construct(Name, Workplace, Home : in String) do
N := To_Unbounded_String(Name);
W := To_Unbounded_String(Workplace);
H := To_Unbounded_String(Home);
end Construct;
--...
end Passenger;
Passenger : Task_Passenger;
begin
Passenger.Construct("Any", "length", "strings!");
--...
end Main;
答案 1 :(得分:2)
Ada并不真正拥有施工人员。在其他语言中,构造函数本质上是一个方法,它接受参数并具有一个使用这些参数填充内容的主体。试图让判别式作为构造函数并不能很好地工作,因为没有子程序体对判别式做任何事情。也许看起来就像它应该的那样,因为语法涉及一个类型,后跟括号中的判别值列表,并用逗号分隔。但那是肤浅的相似之处。判别式的目的不是模仿构造函数。
对于"正常"记录类型,构造函数的最佳替代是返回该类型对象的函数。 (可以认为这类似于使用静态"工厂方法"而不是像Java这样的语言中的构造函数。)该函数可以采用String
参数或任何其他类型的参数。
对于任务类型,它有点棘手,但您可以编写一个返回任务访问权限的函数。
type Passenger_Acc is access all Passenger;
function Make_Passenger (Name : String;
Workplace_Station : String;
Home_Station : String) return Passenger_Acc;
要实现它,您需要在Passenger
任务中定义一个条目(请参阅Roger Wilco的回答),然后您可以在正文中使用它:
function Make_Passenger (Name : String;
Workplace_Station : String;
Home_Station : String) return Passenger_Acc is
Result : Passenger_Acc;
begin
Result := new Passenger;
Result.Construct (Name, Workplace_Station, Home_Station);
return Result;
end Make_Passenger;
(你必须通过返回一个任务访问来实现这一点。我不认为你可以让函数自己返回一个任务,因为你必须使用扩展返回来设置任务对象和任务对象在函数返回之前不会被激活,因此无法接受条目。)
答案 2 :(得分:1)
你说
“说实话,我不明白我该怎么做。过去我曾经使用过几种编程语言,但是把一个简单的String传递给一个构造函数我从来没有遇到过这么多麻烦白痴,但我不明白为什么这么常见的操作会如此复杂,我敢肯定我正在接近这个问题,请赐教我,告诉我这样做的正确方法,因为我疯了:D “
Ada的访问类型通常是混乱的来源。主要问题是Ada没有自动垃圾收集,并且希望确保您不会遇到返回指向局部变量的指针的问题。这两者的结合产生了一系列奇怪的规则,迫使您仔细设计解决方案。
如果您确定您的代码是好的,那么您总是可以在别名字符串上使用'Unrestricted_Access。这样就可以确保所访问的变量不会从任务下面消失。
答案 3 :(得分:0)
它不一定非常复杂。您可以使用匿名访问类型并按需分配字符串,但请考虑您是否真的希望字符串是判别式。
这是一个完整的,有效的例子:
with Ada.Text_IO;
procedure String_Discriminants is
task type Demo (Name : not null access String);
task body Demo is
begin
Ada.Text_IO.Put_Line ("Demo task named """ & Name.all & """.");
exception
when others =>
Ada.Text_IO.Put_Line ("Demo task terminated by an exception.");
end Demo;
Run_Demo : Demo (new String'("example 1"));
Second_Demo : Demo (new String'("example 2"));
begin
null;
end String_Discriminants;
另一个选择是在库级别包中将字符串声明为别名常量,但是你很接近于只有一个枚举的判别式,并且在丢弃它之前应该仔细考虑该选项。
答案 4 :(得分:0)
我认为另一种解决方案如下:
task body Some_Task_That_Tries_To_Use_Passenger is
Name1 : aliased String := "Foo";
Home1 : aliased String := "Man";
Work1 : aliased String := "Bar";
Passenger1 : aliased Passenger(
Name => Name1'Access,
Workplace_Station => Work1'Access,
Home_Station => Home1'Access
);
begin
--...