首页 文章

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

提问于
浏览
0

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

  • 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 的可见部分 .

    EDIT 2: 这里's a solution if you'在实例化时需要 '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功能更简洁地编写,但你明白了 . )

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

    EDIT:

    评论澄清说,以前不是你想要的 . 上面的表单将用于你有一个可能重写的验证器和一个默认的不必使用标记类型(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 );
    
  • 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的通用形式系统


    没有语法突出显示,因为它看起来很糟糕 .

  • 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的代码结构非常明显,如果您尝试使用Ada,那么我将会给您带来很多麻烦/工作,就像它是C风格的语言一样 .

    此外,坐下来,深吸一口气,思考问题空间和建筑物也可能是有益的 .

相关问题