具有默认子程序的Ada通用包

时间:2013-11-14 16:53:45

标签: generics ada

我正在尝试创建一个具有默认值的子程序参数的Ada通用包。我不能让编译器识别默认值..我猜这是由于可见性。有没有办法在泛型声明中转发声明一个函数?

通用规范:

generic
    type Item is private;
    type Item_Ref is access all Item;
    Addr : System.Address;
    Default : Item;

    with Is_Valid (Obj : Item) return Boolean;

    -- Forward Declare ** DOES NOT COMPILE
    function Default_Validate (Data_Ptr : Item_Ref) return Boolean;

    with function Validate (Data_Ptr : Item_Ref) return Boolean is Default_Validate;

package Foo is

    -- function Default_Validate (Data_Ptr : Item_Ref) return Boolean;

    function Read_Eeprom return Item;

end Foo;

通用机构:

package body Foo is

    Obj : aliased Item;
    for Obj'Address use Addr;

    -- Read Method
    function Read_Eeprom return Item is
    begin

        -- ** Read EEPROM using OBJ **

        Validate (Obj'Unchecked_Access);

    end Read_Eeprom;

    -- Default Validate Method
    function Default_Validate (Data_Ptr : Item_Ref) return Boolean is 
        Valid : Boolean;
    begin
        Valid := Is_Valid(Data_Ptr.all);

        if not Valid then
            Data_Ptr.all := Default;
        end if;

        return Valid;
    end Default_Validate;

end Foo;

驱动程序

with Foo;
procedure Main is
    MAX_INT : constant Integer := 100;
    MIN_INT : constant Integer := 0;

    -- Special / Non-Scaler Type
    type Pair_Type is 
        record
            X : Integer;
            Y : Integer;
        end record;

    type Pair_Ref is access all Pair;

    -- Is Valid
    function Int_Is_Valid(Int : Integer) return Boolean is
    begin 
        return (Int <= MAX_INT and Int >= MIN_INT);
    end Pair_Is_Valid;

    -- Is Valid
    function Pair_Is_Valid(Pair : Pair_Type) return Boolean is
    begin 
        return Pair.X'Valid and Pair.Y'Valid;
    end Pair_Is_Valid;

    -- Validate
    function Pair_Validate(Pair : Pair_Ref) return Boolean is
        Valid : Boolean := True;
    begin
        if not Pair.X'Valid then
            Pair.X := 0;
            Valid := False;
        end if;

        if not Pair.Y'Valid then
            Pair.Y := 0;
            Valid := False;
        end if;

        return Valid;
    end Special_Validate;

    type Int_Ref is access all Integer;

    My_Int  : Integer;
    My_Pair : Pair_Type;
    Default_Pair : Pair_Type := (0,0);

    package Int_Obj is new Foo (Item => Integer,
                                Item_Ref => Int_Ref,
                                Addr => My_Int'Address,
                                Default => 0,
                                Is_Valid => Int_Is_Valid);

    package Pair_Obj is new Foo (Item => Pair_Type,
                                 Item_Ref => Pair_Ref,
                                 Addr => My_Pair'Address,
                                 Default => Default_Pair,
                                 Is_Valid => Pair_Is_Valid,
                                 Validate => Pair_Validate);

   Tmp_Int   : Integer;
   Tmps_Pair : Pair_Type;

begin

   Tmp_Int := Int_Obj.Read_Eeprom;
   Tmp_Pair := Pair_Obj.Read_Eeprom;

end Main;

我得到的错误是“预期文件结束,文件只能有一个编译单元” 如何将通用子程序默认为属于包的成员的函数?

4 个答案:

答案 0 :(得分:1)

不幸的是,你不能 - 这是一个鸡与蛋的问题。编译器需要弄清楚所有通用参数在实例化泛型之前会是什么;但是{<1}}方法在实例化 之后才会变为可用。我认为你最接近的是声明两个泛型:

Default_Validate

(我还没有测试过。编辑:测试,编译好。)我假设Foo中唯一公开可见的东西是程序和功能。如果有其他重要功能(例如类型),它会变得更复杂,然后您可能必须使用嵌套泛型,其中generic type Item is private; type Item_Ref is access all Item; with function Validate (Data_Ptr : Item_Ref) return Boolean; package Foo is function Default_Validate (Data_Ptr : Item_Ref) return Boolean; -- etc. end Foo; generic type Item is private; type Item_Ref is access all Item; package Foo_With_Default_Validator is -- important procedure/function declarations from Foo end Foo_With_Default_Validator; package body Foo_With_Default_Validator is function Default_Validate (Data_Ptr : Item_Ref) return boolean; package My_Foo is new Foo(Item, Item_Ref, Default_Validate); function Default_Validate (Data_Ptr : Item_Ref) return boolean renames My_Foo.Default_Validate; -- and other procedures/functions will be renames of things from My_Foo end Foo_With_Default_Validator; 从外部泛型移动到内部泛型,或者您可能能够使用通用的正式包将通用分为两部分。在任何一种情况下,泛型的用户可能必须执行两个实例化。如果上述解决方案有效,那么用户将实例化with function ValidateFoo,但它将是一个或另一个 - 不需要两个实例化。如果您需要更多帮助,我认为我们需要查看Foo_With_Default_Validator的可见部分。

编辑2:如果您在实例化时愿意要求Foo属性,那么这是一个解决方案:

'Access

然后在generic type Item is private; type Item_Ref is access all Item; Validate : access function (Data_Ptr : Item_Ref) return Boolean := null; package Foo is function Default_Validate (Data_Ptr : Item_Ref) return Boolean; -- etc. end Foo; 的正文中,你需要一个像这样的函数:

Foo

每当您想要调用验证函数时,从身体的其余部分调用function Perform_Validate (Data_Ptr : Item_Ref) return Boolean is begin if Validate = null then return Default_Validate (Data_Ptr); else return Validate (Data_Ptr); end if; end Perform_Validate; 。 (Perform_Validate可以使用新的Ada 2012功能更简洁地编写,但你明白了。)

答案 1 :(得分:0)

如你所知,通用版定义了一个函数Default_Validate,因为关键字 function 前面有 { {1}} 即可。你应该拥有的是:

with

修改

评论澄清说,以前不是你想要的。上面的表单将用于你有一个可能重写的验证器和默认的验证器,而不必使用标记类型(Ada的术语用于OOP类的实例)。

而你想要的是参数可能从可见的子程序中获取子程序作为形式参数的默认值,以下是这样的:

generic
    type Item is private;
    type Item_Ref is access all Item;

    with function Default_Validate (Data_Ptr : Item_Ref) return Boolean;
    -- A function "Validate", which defaults to 'Default_Validate'.
    with function Validate (Data_Ptr : Item_Ref) return Boolean is Default_Validate;
package Foo is
    -- function Default_Validate (Data_Ptr : Item_Ref) return Boolean;
end Foo;

但即使这样也可能会有所改善:

generic
    type Item is private;
    type Item_Ref is access all Item;

    -- A function "Validate", which defaults to 'Validate'. The function
    -- needs to be visible when the generic is instantiated, not here
    -- where the generic is defined.
    with function Validate (Data_Ptr : Item_Ref) return Boolean is <>;
package Foo is
    -- Just a stub.
end Foo;

    Type Some_Integer_Access is access all Integer;
    function Validate (Data_Ptr : Some_Integer_Access) return Boolean is (true);


    Package K is new Foo( Item     => Integer,
                          Item_Ref => Some_Integer_Access
            );

答案 2 :(得分:0)

  

我不确定这是否有效。看来我必须为泛型的每个实例定义一个验证函数,即使我没有将它作为参数传递。

这可能是最好的方法来解决这个问题;毕竟你正在谈论数据的验证......以及可能有意外行为的默认行为。 (任何进出系统的类型都应该被验证;例如文件读取,数据库读取,用户输入等。)

  

在泛型中使用默认函数的目的是消除重复的代码。还有其他想法吗?

嗯,有一种方法让人想起。它要求我们限制我们在通用中接受的内容。我的其他解决方案[几乎]完全一般化,因此适用于任何非限制类型。

根据'Valid属性,我们看到documentation说明了这一点:
The Valid attribute can be used to check the validity of data produced by unchecked conversion, input, interface to foreign languages, and the like.

所以我们有一个默认的验证 我们还有一种方法可以将属性置于函数形式参数的默认值中 我们有一种默认可见功能的方法 最后我们有嵌套泛型。

现在没有通用的正式类型scalar,这在这里很有用......但我们有办法限制形式参数的类型Type T(<>) is (<>)仅限于整数数字,模块类型和枚举...我们所知道的所有都是标量类型。

Generic
    Type Item(<>) is (<>);
Package Generic_Base is
    -- To use this as a Ada-95 pkg delete everything after 'Boolean',
    -- create a body and return X'Valid from the implementation.
    Function Default_Validate(X : Item) Return Boolean is (X'Valid);

    Generic
        with function Validate(X:Item) return Boolean is Default_Validate;
    Package Generic_Nested is
        -- Operations requiring validation.
    End Generic_Nested;

End Generic_Base;

使用这些包将如下:

Package Base is new Generic_Base( Integer );
Package Nested is new Base.Generic_Nested;

如果你很乐意将形式参数限制为积分/模块/枚举,那么这应该可行。

所以,你去吧。


推荐阅读:

Ada's Generic Formal Type System


没有语法突出显示,因为它看起来像 可怕

答案 3 :(得分:0)

你真的继续移动球门柱。总而言之,你的新增内容是可怕的:它们根本不编译,它们是明显剪切和粘贴代码的一小部分,如功能名称不匹配所证明的那样(例如Int_Is_Valid / {{1} })。

首先,让我们使用签名包。

Pair_Is_Valid

signature.ads

generic Type Item is private; Default : in Item; package SIGNATURE is end SIGNATURE;

foo.ads

with System, SIGNATURE; generic with package Item_Pkg is new SIGNATURE(<>); Addr : System.Address; with function Is_Valid(X : Item_Pkg.Item) return Boolean is <>; package Foo is use Item_Pkg; function Read_Eeprom return Item; function Is_Valid (Data_Ptr : access Item) return Boolean; private Port : Item; pragma Volatile( Port ); Pragma Import( Convention => Ada, Entity => Port ); For Port'Address Use Addr; end Foo;

foo.adb

package body Foo is function Read_Eeprom return Item is Result : constant Item:= Port; begin if Is_Valid(Result) then return Result; else return Default; end if; end Read_Eeprom; function Is_Valid (Data_Ptr : access Item) return Boolean is begin return Is_Valid(Data_Ptr.all); end Is_Valid; end Foo;

driver.ads

package Driver is MAX_INT : constant Integer := 100; MIN_INT : constant Integer := 0; -- Special / Non-Scaler Type type Pair_Type is record X : Integer; Y : Integer; end record; -- Is Valid **USING OVERLOADS** function Is_Valid(Int : Integer ) return Boolean; function Is_Valid(Pair : Pair_Type) return Boolean; My_Int : Integer; My_Pair : Pair_Type; private Default_Pair : constant Pair_Type := (0,0); Default_Integer : constant Integer := 0; end Driver;

driver.adb

考虑到围绕with Foo, SIGNATURE; package body Driver is -- Is Valid function Is_Valid(Int : Integer) return Boolean is (Int <= MAX_INT and Int >= MIN_INT); function Is_Valid(Pair : Pair_Type) return Boolean is (Pair.X'Valid and Pair.Y'Valid); package Int_pkg is new SIGNATURE(Integer, 0); package Pair_Pkg is new SIGNATURE(Pair_Type, Default_Pair); -- Using defaults for Is_Valid. package Int_Obj is new Foo (Item_Pkg => Int_Pkg, Addr => My_Int'Address ); package Pair_Obj is new Foo(Item_Pkg => Pair_Pkg, Addr => My_Pair'Address ); end Driver; 类型的代码的相当明显的结构,我猜你正试图从C或C ++“导入”知识。如果您尝试使用Ada,那将会给您带来很多麻烦/工作,就像它是C风格的语言一样。

此外,坐下来,屏住呼吸,思考问题空间和建筑物in terms of types可能是有益的。