如何绕过 Ada 中标记记录的禁止判别式默认值?
How to get around forbidden discriminants defaults for tagged records in Ada?
我正在学习 Ada,但遇到了设计问题。对不起,我不了解基本的 Ada 机制和习语。
假设我想表示一个操作。运算符可以是 plus
或 minus
,操作数可以是整数或字符串。
免责声明:有些事情在语义层面上可能没有多大意义(minus
字符串,没有操作数的运算符,......)但这都是关于表示的。
目前我有以下不正确代码:
operand.ads:
package Operand is
-- I want a None for unary operands or operators without operands
type Operand_Type is (Int, Str, None);
-- This needs to be tagged
type Instance (Op_Type : Operand_Type := None) is tagged record
case Op_Type is
when Int =>
Int_Value : Integer;
when Str =>
Str_Value : String (1 .. 10);
when None =>
null;
end case;
end record;
-- Some methods on operands...
end Operand;
operation.ads:
with Operand;
package Operation is
type Operation_Type is (Plus, Minus);
-- This also needs to be tagged
type Instance is tagged record
Left, Right : Operand.Instance;
end record;
-- Some methods on operations...
end Operation;
main.adb:
with Operand;
with Operation;
procedure Main is
Op : Operation.Instance;
begin
Op.Left := (Op_Type => Operand.Int, Int_Value => 1);
Op.Right := (Op_Type => Operand.Int, Int_Value => 3);
end Main;
当我尝试编译时出现以下错误:
$ gnatmake main.adb
gcc -c main.adb
operand.ads:7:45: discriminants of nonlimited tagged type cannot have defaults
operation.ads:9:28: unconstrained subtype in component declaration
gnatmake: "main.adb" compilation error
我明白为什么我不能在标记类型的判别式上使用默认值,但我真的不知道如何绕过这个限制。
提案 1:
停止使用变体记录并为每个操作数使用一个字段的记录。但我觉得这只是抛弃了代码的优雅。
提案 2:
从 Operand.Instance
记录中删除默认值并从 Operation.Instance
记录中限制 Left
和 Right
。但是我收到运行时错误:
raised CONSTRAINT_ERROR : main.adb:7 discriminant check failed
因为我无法动态更改记录字段的判别值。
如有任何帮助,我们将不胜感激!
Jim Rogers 已经讨论过使用继承。如果愿意,您还可以通过创建内部非标记类型(允许默认值)来使用组合,将 Operand.Instance 类型标记为私有,让私有实现使用内部非标记版本,然后添加您需要的操作设置和获取操作数:
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
package Operand is
-- I want a None for unary operands or operators without operands
type Operand_Type is (Int, Str, None);
Invalid_Operand : exception;
-- This needs to be tagged
type Instance is tagged private;
function Int_Value(Value : Integer) return Instance;
function Str_Value(Value : String) return Instance;
function Null_Instance return Instance;
function Int_Value(Self : Instance) return Integer;
function Str_Value(Self : Instance) return String;
function Op_Type(Self : Instance) return Operand_Type;
-- Some methods on operands...
private
type Instance_Internal (Op_Type : Operand_Type := None) is record
case Op_Type is
when Int =>
Int_Value : Integer;
when Str =>
Str_Value : String (1 .. 10);
when None =>
null;
end case;
end record;
type Instance is tagged record
Impl : Instance_Internal;
end record;
function Int_Value(Value : Integer) return Instance is (Impl => (Int, Value));
function Str_Value(Value : String) return Instance is (Impl => (Str, Value));
function Null_Instance return Instance is (Impl => (Op_Type => None));
function Int_Value(Self : Instance) return Integer
is (if Self.Impl.Op_Type = Int then Self.Impl.Int_Value else raise Invalid_Operand);
function Str_Value(Self : Instance) return String
is (if Self.Impl.Op_Type = Str then Self.Impl.Str_Value else raise Invalid_Operand);
function Op_Type(Self : Instance) return Operand_Type
is (Self.Impl.Op_Type);
end Operand;
package Operation is
type Operation_Type is (Plus, Minus);
-- This also needs to be tagged
type Instance is tagged record
Left, Right : Operand.Instance;
end record;
-- Some methods on operations...
end Operation;
Op : Operation.Instance;
begin
Put_Line("Hello, world!");
Op.Left := Operand.Int_Value(1);
Op.Right := Operand.Int_Value(3);
Put_Line(Integer'Image(Op.Left.Int_Value));
Put_Line(Integer'Image(Op.Right.Int_Value));
end Hello;
您可以将 Operand 包分解为 spec 和 body 以获得更好的可读性,这只是为了举例。
我正在学习 Ada,但遇到了设计问题。对不起,我不了解基本的 Ada 机制和习语。
假设我想表示一个操作。运算符可以是 plus
或 minus
,操作数可以是整数或字符串。
免责声明:有些事情在语义层面上可能没有多大意义(minus
字符串,没有操作数的运算符,......)但这都是关于表示的。
目前我有以下不正确代码:
operand.ads:
package Operand is
-- I want a None for unary operands or operators without operands
type Operand_Type is (Int, Str, None);
-- This needs to be tagged
type Instance (Op_Type : Operand_Type := None) is tagged record
case Op_Type is
when Int =>
Int_Value : Integer;
when Str =>
Str_Value : String (1 .. 10);
when None =>
null;
end case;
end record;
-- Some methods on operands...
end Operand;
operation.ads:
with Operand;
package Operation is
type Operation_Type is (Plus, Minus);
-- This also needs to be tagged
type Instance is tagged record
Left, Right : Operand.Instance;
end record;
-- Some methods on operations...
end Operation;
main.adb:
with Operand;
with Operation;
procedure Main is
Op : Operation.Instance;
begin
Op.Left := (Op_Type => Operand.Int, Int_Value => 1);
Op.Right := (Op_Type => Operand.Int, Int_Value => 3);
end Main;
当我尝试编译时出现以下错误:
$ gnatmake main.adb
gcc -c main.adb
operand.ads:7:45: discriminants of nonlimited tagged type cannot have defaults
operation.ads:9:28: unconstrained subtype in component declaration
gnatmake: "main.adb" compilation error
我明白为什么我不能在标记类型的判别式上使用默认值,但我真的不知道如何绕过这个限制。
提案 1:
停止使用变体记录并为每个操作数使用一个字段的记录。但我觉得这只是抛弃了代码的优雅。
提案 2:
从 Operand.Instance
记录中删除默认值并从 Operation.Instance
记录中限制 Left
和 Right
。但是我收到运行时错误:
raised CONSTRAINT_ERROR : main.adb:7 discriminant check failed
因为我无法动态更改记录字段的判别值。
如有任何帮助,我们将不胜感激!
Jim Rogers 已经讨论过使用继承。如果愿意,您还可以通过创建内部非标记类型(允许默认值)来使用组合,将 Operand.Instance 类型标记为私有,让私有实现使用内部非标记版本,然后添加您需要的操作设置和获取操作数:
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
package Operand is
-- I want a None for unary operands or operators without operands
type Operand_Type is (Int, Str, None);
Invalid_Operand : exception;
-- This needs to be tagged
type Instance is tagged private;
function Int_Value(Value : Integer) return Instance;
function Str_Value(Value : String) return Instance;
function Null_Instance return Instance;
function Int_Value(Self : Instance) return Integer;
function Str_Value(Self : Instance) return String;
function Op_Type(Self : Instance) return Operand_Type;
-- Some methods on operands...
private
type Instance_Internal (Op_Type : Operand_Type := None) is record
case Op_Type is
when Int =>
Int_Value : Integer;
when Str =>
Str_Value : String (1 .. 10);
when None =>
null;
end case;
end record;
type Instance is tagged record
Impl : Instance_Internal;
end record;
function Int_Value(Value : Integer) return Instance is (Impl => (Int, Value));
function Str_Value(Value : String) return Instance is (Impl => (Str, Value));
function Null_Instance return Instance is (Impl => (Op_Type => None));
function Int_Value(Self : Instance) return Integer
is (if Self.Impl.Op_Type = Int then Self.Impl.Int_Value else raise Invalid_Operand);
function Str_Value(Self : Instance) return String
is (if Self.Impl.Op_Type = Str then Self.Impl.Str_Value else raise Invalid_Operand);
function Op_Type(Self : Instance) return Operand_Type
is (Self.Impl.Op_Type);
end Operand;
package Operation is
type Operation_Type is (Plus, Minus);
-- This also needs to be tagged
type Instance is tagged record
Left, Right : Operand.Instance;
end record;
-- Some methods on operations...
end Operation;
Op : Operation.Instance;
begin
Put_Line("Hello, world!");
Op.Left := Operand.Int_Value(1);
Op.Right := Operand.Int_Value(3);
Put_Line(Integer'Image(Op.Left.Int_Value));
Put_Line(Integer'Image(Op.Right.Int_Value));
end Hello;
您可以将 Operand 包分解为 spec 和 body 以获得更好的可读性,这只是为了举例。