1

我正在尝试创建一个具有默认值的子程序参数的 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

4 回答 4

1

不幸的是,你不能——这是一个先有鸡还是先有蛋的问题。编译器需要在实例化泛型之前弄清楚所有泛型参数将是什么;但是在泛型实例化之前,该方法Default_Validate不可用。我认为最接近的方法是声明两个泛型:

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;

(我还没有对此进行测试。 编辑:经过测试,编译正常。)我在这里假设 Foo 中唯一公开可见的东西是过程和函数。如果还有其他重要功能(例如类型),它会变得更加复杂,然后您可能必须使用嵌套泛型,with function Validate将外部泛型移到内部泛型中,或者您可以使用泛型正式包将泛型分成两部分。在任何一种情况下,泛型的用户可能必须执行两个实例化。如果上述解决方案有效,那么用户将实例化FooFoo_With_Default_Validator,但它会是一个或另一个 - 不需要两个实例化。如果您需要更多帮助,我认为我们需要查看Foo.

编辑2:如果您愿意'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在您想调用验证函数时从身体的其余部分调用。(Perform_Validate可以使用新的 Ada 2012 功能更简洁地编写,但你明白了。)

于 2013-11-14T18:50:28.647 回答
0

你真的一直在移动球门柱。总之,您的新增功能很糟糕:它们根本无法编译,而且它们只是一些明显剪切和粘贴的代码,这可以通过不匹配的函数名称(例如Int_Is_Valid/ 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;

鉴于您的代码围绕access-types 的结构相当明显,我猜您正在尝试从 C 或 C++ 中“导入”知识。如果您尝试像使用 C 风格语言一样使用 Ada,那将会给您带来很多麻烦/工作。

此外,坐下来,深呼吸,根据问题空间和架构的类型来思考问题可能是有益的。

于 2013-11-19T23:23:19.123 回答
0

正如您所拥有的,泛型定义了一个函数,,Default_Validate因为关键字function前面没有. with你应该拥有的是:

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;

编辑:

评论澄清说,以前不是你想要的。如果您有一个可能覆盖的验证器和一个默认的验证器,而无需使用标记类型(Ada 的 OOP 类实例术语),则使用上面的形式。

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

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
            );

但即使这样也可以改进:

generic
    type Item is private;
    with function Validate (Data_Ptr : not null access Item) return Boolean is <>;
package Foo is
    -- Just a stub.
end Foo;


    function Validate (Data_Ptr : not null access Integer) return Boolean is (true);

    -- One parameter! Ah! Ah, Ah!
    Package K is new Foo( Item => Integer );
于 2013-11-14T20:10:51.700 回答
0

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

这可能是解决这个问题的最佳方法;毕竟,您正在谈论数据验证......并且默认可能会产生意外行为。(任何进入/离开系统的类型都应该经过验证;例如文件读取、数据库读取、用户输入等)

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

好吧,我想到了一种方法。它要求我们限制我们在通用中接受的内容。我的其他解决方案 [几乎] 完全通用,因此适用于任何非限制类型。

采用属性'Valid,我们看到文档是这样说的:
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 的通用形式类型系统


没有语法高亮,因为它看起来很糟糕

于 2013-11-15T23:28:49.740 回答