如何使变量只读,但不是常数?

时间:2017-02-24 11:40:12

标签: design-patterns ada

提供对变量的只读访问权限(当然?)可以通过抽象实现。例如,我可以将变量设置为可调用实体或泛型的 in 模式参数。然后,变量的使用(通过这些常量视图)将被限制在可调用或通用实例中。

这个结构不容易添加到现有程序中,我认为,因为该程序已经结构化;此外,它不是一个独立的解决方案,因为它需要在“只读”和结构之间进行耦合。

另一个选择是使变量为private并导出一个返回其值的函数。但是,我想要直接曝光,例如 volatile 常量,从不同的角度来看仍然是一个变量。

我想出了一个叠加层:

with Interfaces;

package Read_Only is
   subtype Pins is Interfaces.Unsigned_16;

   V : constant Pins with Volatile, Import;

private
   Backing : Pins with Volatile;
   for V'Address use Backing'Address;
   procedure Reset;
end Read_Only;

这会屏蔽V,以便只有包体(和子)可以修改其值,而包的客户端可以读取V。但是“隐藏”所有这些背后的方面和地址让我想到:还有其他一些更明显的方法吗?

修改:通过@ flyx的评论提醒,该软件包的公开部分的读者会看到constant并且可能认为V在物理上是恒定的 - 它是不是,volatile。我更希望能够保留两者 V的类似对象字符以及无法从外部Read_Only更改的事实。 V这里实际上并不是一个常量对象,但它的声明是这样说的。我想我想声明一个可识别的易变物体或一些物体的恒定视图,而不会产生函数的偶然性。

2 个答案:

答案 0 :(得分:3)

我的建议是,使用一个简单的函数和一个私有变量:

with Interfaces;

package Read_Only is
   subtype Pins is Interfaces.Unsigned_16;

   function V return Pins;

private
   Backing : Pins;
   function V return Pins is (Backing);
end Read_Only;

Ada的调用约定将确保以最有效的方式返回对象。

此外,将其实现为表达式函数将确保调用内联。如果您希望直接曝光并且在任何情况下都不需要调用,即使您在没有优化的情况下进行编译,也可以使用Inline_Always方面:

function V return Pins with Inline_Always;

在这种情况下,调用始终内联到访问的变量,因此就发出的代码而言,它完全等同于直接访问。

编辑:对不起,我刚看到你不想要一个功能。鉴于上述情况,我不明白为什么会这样。你能给出一个更准确的理由吗?

答案 1 :(得分:1)

有趣的问题。

  

提供对变量的只读访问权限(当然?)可以通过抽象实现。例如,我可以将变量设置为可调用实体或通用实体的模式参数。然后,变量的使用(通过这些常量视图)将被限制在可调用或通用实例中。

     

这个结构不容易添加到现有程序中,我认为,因为该程序已经结构化;此外,它不是一个独立的解决方案,因为它需要在“只读”和结构之间进行耦合。

我不确定仿制药会有多难,但如果您愿意重新考虑仿制药的使用,我认为您可以这样做:

Generic
    Type Alpha(<>) is private;
Package Constant_Access is
    Type    Beta  is access constant Alpha;
    Subtype Gamma is not null Beta;

    Generic
        Item_Access : in Gamma;
    Package Access_Accessor is
        Item : Constant Alpha renames Item_Access.all;
        Pragma Volatile( Item );
    End Access_Accessor;

End Constant_Access;

然后,当您需要提供一个仅限常量的视图时,在特定类型的Access_Accessor实例化上使用Constant_Access实例化 - 然后,在该实例化之后立即重命名实例化Item到要隐藏/保持不变的值的名称。

是的,这有点尴尬,但它确实确保你想要的属性(即,可能是不稳定的),即使面对大量的优化。

<强>编辑:

With
System.Address_To_Access_Conversions;

Generic
    Type Alpha(<>) is private;
Package Constant_Access with Preelaborate is

    Package Conversions is new System.Address_To_Access_Conversions( Alpha );
    Use Conversions, System;

    -----------------------
    -- Type Declarations --
    -----------------------
    Type    Beta    is access constant Alpha;
    Subtype Gamma   is not null Beta;
    Type    Epsilon( G : Not Null Access Constant Alpha ) is null record
        with Implicit_Dereference => G, Volatile;
    -- NOTE: You could excise Beta and Gamma, they are included for flexibility.

    --------------------------
    -- Conversion Functions --
    --------------------------

    Function "+"( Right : Aliased Alpha ) return Gamma is
      ( Right'Access );
    Function "+"( Right : Gamma ) return Alpha is
      ( Right.All );
    Function "+"( Right : Gamma ) return Epsilon is
      ( G => Right.All'Access );
    Function "+"( Right : Epsilon ) return Gamma is
      ( Right.G );
    Function "+"( Right : Aliased Alpha ) return Epsilon is
      ( G => Right'Access );
    Function "+"( Right : Epsilon ) return Alpha is
      ( Right.G.All ); -- Not actually needed due to implicit dereferencing.
    Function "+"( Right : Not Null Access Constant Alpha ) return Address is
      ( Right.All'Address );
    Function "+"( Right : Address ) return Alpha is
      ( To_Pointer(Right).All );
    Function "+"( Right : Epsilon ) return Address is
      ( +Right.G );
    Function "+"( Right : Address ) return Epsilon is
      ( G => +To_Pointer(Right).All );    

    -----------------------
    -- Accessor Generics --
    -----------------------
    Generic
        Item_Access : in Gamma;
    Package Access_Accessor is
        Item : Epsilon( Item_Access )
          with Volatile;
    End Access_Accessor;

    Generic
        Item_Address : in System.Address;
    Package Address_Accessor is
        Item : Epsilon := +Item_Address
          with Volatile;
    End Address_Accessor;

End Constant_Access;