假设我拥有世界上最愚蠢的环形缓冲区。
size: constant := 16;
subtype T is integer;
package RingBuffer is
procedure Push(value: T);
function Pop return T;
end;
package body RingBuffer is
buffer: array(0..size) of T;
readptr: integer := 0;
writeptr: integer := 1;
procedure Push(value: T) begin
buffer(writeptr) := value;
writeptr := (writeptr + 1) mod size;
end;
function Pop return T
begin
readptr := (readptr + 1) mod size;
return buffer(readptr);
end;
end;
因为我的代码很糟糕,我想添加前提条件和后置条件,以免我误用这个。所以我按如下方式更改了Push的实现:
procedure Push(value: T) with
pre => readptr /= writeptr
is begin
buffer(writeptr) := value;
writeptr := (writeptr + 1) mod size;
end;
但是,我收到编译错误,因为我需要将方面定义放在过程的声明中,而不是在实现中。
问题是,这是一个包。我的声明是公开的。前置条件所依赖的值属于包体,声明不可见。为了将方面定义放在声明中,我将不得不重构我的代码以将实现细节公开到包的公共部分(在这种情况下,readptr
和writeptr
)。我不想这样做。
我可以想到一些解决这个问题的方法,例如让Push()
实现PushImpl()
调用私有{{1}}过程只定义在实际具有前提条件的主体中......但这太可怕了。什么是正确的方法?
答案 0 :(得分:5)
我认为当验证检查是私有的时,这总是会出现问题,并且解决方案是声明一个函数来进行检查:
package RingBuffer is
function Is_Full return Boolean;
procedure Push(value: T) with Pre => not Is_Full;
function Pop return T;
(Is_Full
无论如何都可能有用;在其他情况下可能不是这样。)
如果将实现保留在包体中,则还需要将Is_Full
放在那里,但您可以将它们移动到规范并使用表达式函数:
package RingBuffer is
function Is_Full return Boolean;
procedure Push(value: T) with Pre => not Is_Full;
function Pop return T;
private
buffer: array(0..size) of T;
readptr: integer := 0;
writeptr: integer := 1;
function Is_Full return Boolean is (Readptr = Writeptr);
end RingBuffer;
答案 1 :(得分:2)
合同方面旨在用于(子)类型和子程序的公共视图。
如果要将检查保留在私有视图中,则将其编写为子程序中的第一个语句很简单:
begin
if Is_Full then
raise Constraint_Error with "Ring buffer is full.";
end if;
...
一些未经请求的建议:
Is_Empty
检查。type Indexes is mod 16;