2017-02-24 97 views
1

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

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

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

我想出了一个覆盖:

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这里实际上并不是一个常量对象,但它的声明是这样说的。我想我想宣布对一个可识别的易变对象或某个对象的持续观点,而不会引发功能的突发事件。

+0

难道编译器允许基于这样的事实,'V'是'constant',这将打破你的代码做一些优化,因为它不是实际上不变?我也会说一个函数是最好的方法,因为它告诉用户和编译器该值可能会随时间而改变。 – flyx

+0

我不认为编译器可以优化,如果有的话(你有什么想法?),在** volatile **的存在 - 尽管它可能内联一个函数返回一个副本或引用(如果可能的话)一个易变的变量。但是,如何优化带走**常数**?我会说,这个编译器会被严重破坏。此外,** volatile **可以防止软件包成为“Pure”,而通常情况下,Ada函数在纯软件包中更是如此,它是实际上随时间变化的值的微弱指标,即使是没有参数的值也是如此。但这是问题或愿望的一部分:“常量”≠“只读”。 – B98

+0

我同意'Volatile'将最有可能禁止不断传播。这只是一个想法,我没有记住LRM。不过,当我这样做时,我的GNAT发出*警告:常量覆盖变量*。 – flyx

回答

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; 

阿达的调用约定将确保对象是在最有效的方式反正返回。

此外,将其作为表达式函数实现将确保调用的内联。

function V return Pins with Inline_Always; 

在这种情况下,呼叫总是被内联了访问的变量,所以在条款:如果你想直接暴露和在任何情况下没有呼叫,甚至当你编译不优化您也可以使用Inline_Always方面发出的代码严格相当于直接访问。

编辑:对不起,我刚才看到你不想要一个函数。鉴于上述,我不明白为什么。你能给出一个更精确的理由吗?

+2

我不认为表达函数必须内联。我可以看到,如果它选择这样做,它可能会使编译器更容易。 'Inline_Always'是GNAT中定义的实现。 –

+0

可能的原因包括:让客户端控制volatile变量的读取次数;推迟建立一个完全隐藏变量的接口 - 一个函数将是中间立场,但如果不是“V”的副本类型,可能会引起一个易失性变量的区分,因为它可能成为通过引用,所以更多想一想; ......这个需求适用于@SimonWright观察到的内联,可能会被视为模糊,这取决于谁被“阻止”直接查看“V”。 – B98

+0

@SimonWright好点。当他们的身体可见时,Expesssion功能保证内联。 –

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; 
+0

我正在考虑访问常量,但到目前为止,并不是关于使用泛型另外!到目前为止,我仍然无法完成工作。 'Access_Accessor'需要一些调整,因为**常量**不能是重命名(次要)的一部分,并且'编译器告诉我'不能将'Volatile'添加到'Item'中。有趣的是,它还指出[RM C.6(13)](http://www.ada-auth.org/standards/aarm12_w_tc1/html/AA-C-6.html#p13)。我目前尝试使用您的解决方案的[gist](https://gist.github.com/98B/74ce568df4028260a75a8b1bf05cf5ed)。 – B98

+0

@ B98 - 这就是我没有通过编译器首先运行它。 无论如何,我已经添加了一个扩展/编辑(并可编译)的块,它可以为您提供所需的所有灵活性。 [嗯,你不能在* Pure *单位中使用它。] – Shark8