简体   繁体   English

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

[英]Ada Generic Package with Default Subprogram

I am trying to create an Ada generic package that has a subprogram parameter with a default value. 我正在尝试创建一个Ada通用软件包,该软件包具有一个带有默认值的子程序参数。 I cant get the compiler to recognize the default value.. Im guessing this is due to visibility. 我无法让编译器识别默认值。我猜这是由于可见性。 Is there a way to forward declare a function within the generic declaration? 有没有办法在通用声明中转发声明一个函数?

The Generic Spec: 通用规格:

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;

The Generic Body: 通用机构:

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;

Driver 司机

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;

The error Im getting is "end of file expected, file can only have one compilation unit" How can I default a generic subprogram to a function that is a member of the package? 我得到的错误是“预期文件结尾,文件只能有一个编译单元”如何将通用子程序默认为包成员的函数?

Unfortunately, you can't--it's a chicken-and-egg problem. 不幸的是,你不能-这是个鸡与蛋的问题。 The compiler needs to figure out what all the generic parameters are going to be before it can instantiate the generic; 编译器需要在实例化泛型之前弄清楚所有泛型参数将是什么。 but the Default_Validate method will not become available until after the generic is instantiated. 但是Default_Validate方法实例化泛型之后才可用。 The closest I think you can come is to declare two generics: 我认为您可以最接近地声明两个泛型:

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;

(I haven't tested this yet. EDIT: tested, compiles OK.) I'm assuming here that the only publicly visible things in Foo are procedures and functions. (我还没有测试过。 编辑:经过测试,可以编译。)在这里,我假设Foo中唯一公开可见的东西是过程和函数。 If there are other important features (such as types), it gets more complicated, and then you might have to use nested generics, where with function Validate gets moved off the outer generic into an inner generic, or you might be able to use a generic formal package to split the generic into two parts. 如果还有其他重要的功能(例如类型),它会变得更加复杂,那么您可能必须使用嵌套的泛型,其中with function Validate可以将外部泛型移入内部泛型,或者您可以使用泛型正式软件包,将泛型分为两个部分。 In either of those cases, the user of the generic might have to perform two instantiations. 在任何一种情况下,泛型的用户可能必须执行两个实例化。 If the above solution works, then a user will instantiate either Foo or Foo_With_Default_Validator , but it would be one or the other--two instantiations wouldn't be needed. 如果上述解决方案有效,则用户将实例化FooFoo_With_Default_Validator ,但这将是一个实例或另一个实例-不需要两个实例化。 If you need more assistance, I think we'd need to see the visible part of Foo . 如果您需要更多帮助,我认为我们需要查看Foo的可见部分。

EDIT 2: Here's a solution if you're willing to require an 'Access attribute when instantiating: 编辑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;

Then in the body of Foo , you'll want a function like this: 然后在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;

and call Perform_Validate from the rest of the body whenever you want to call the validation function. 并在需要调用验证函数时从正文的其余部分调用Perform_Validate ( Perform_Validate could be written more concisely using new Ada 2012 features, but you get the idea.) Perform_Validate可以使用Ada 2012的新功能更简洁地编写,但是您明白了。)

As you have it, the generic is defining a function, Default_Validate , because keyword function is not preceded by with . 当你拥有了它,一般是定义一个函数, Default_Validate ,因为关键字function 被前面with What you should have is this: 您应该拥有的是:

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: 编辑:

The comments clarified that the previous was not what you want. 这些评论澄清说,前一个不是您想要的。 The form above would be used in cases where you have a possibly-overriding validator and a default one w/o having to resort to using tagged types (Ada's terminology for instances of OOP classes). 上面的表格将用于以下情况:您有一个可能覆盖的验证器,并且默认情况下没有验证器而不得不使用标记类型(Ada的OOP类实例的术语)。

Instead what you want is for the parameters to possibly take subprograms from the visible subprograms as defaults for the formal parameters, the following does so: 取而代之的是,您希望参数可以将可见子程序中的子程序作为形式参数的默认值,请执行以下操作:

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

But even this might be improved: 但是,即使这可能会得到改善:

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

I'm not sure this works either. 我也不知道这行得通。 It would seem I would have to define a validate function for each instance of a generic even if I'm not passing it in as a parameter. 似乎我必须为泛型的每个实例定义一个validate函数,即使我没有将其作为参数传递也是如此。

That is probably the best way to approach this; 这可能是解决此问题的最佳方法。 you're talking validation of data after all... and defaulting that could have unexpected behavior. 您毕竟是在验证数据...并默认为可能有意外行为的数据。 (Any type that's crossing in/out of your system ought to be validated; eg File reads, DB reads, user-input, etc.) (应该验证进入系统的任何类型;例如,文件读取,数据库读取,用户输入等)

The point of having the default function in the generic was to eliminate duplicate code. 在泛型中具有默认功能的目的是消除重复的代码。 Any other thoughts? 还有其他想法吗?

Well, there is one method that springs to mind. 好吧,有一种方法可以想到。 It requires us to limit what we accept in our generic though. 它要求我们限制在通用中接受的内容。 My other solutions are [almost] fully generalized and therefore would work on any non-limited type. 我的其他解决方案几乎是完全概括的,因此适用于任何非限制类型。

Taking the attribute 'Valid we see that the documentation says this: 将属性设为'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.

So we have a default validation, of sorts. 因此,我们有各种默认验证。
We also have a way to have attributes stand in for the default of function formal-parameters. 我们也有一种方法可以使属性代替函数形式参数的默认值。
We have a way to default to visible functions. 我们有一种默认设置为可见功能的方法。
Last we have nested generics. 最后,我们嵌套了泛型。

Now there's no generic formal type scalar , which would be useful here... but we have a way to limit the type of a formal parameter Type T(<>) is (<>) is limited to integral numerics, modular-types, and enumerations... all of which we know are scalar-types. 现在没有通用的形式类型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;

Using these packages would be as follows: 使用这些软件包的方法如下:

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

If you're happy in limiting the formal parameter to integrals/modulars/enumerations, this should work. 如果您乐意将形式参数限制为整数/模数/枚举,则应该可以使用。

So, there you go. 所以,你去了。


Recommended reading: 推荐阅读:

Ada's Generic Formal Type System Ada的通用形式类型系统


No syntax highlighting because it looks terrible on this. 没有语法突出显示,因为它看起来很糟糕

You really keep moving the goalposts. 您确实一直在移动球门柱。 Your new additions are, in a word, terrible: they don't compile at all and they are a smattering of obviously cut-and-pasted code as evidenced by mismatching function-names (eg Int_Is_Valid / Pair_Is_Valid ). 总而言之,您的新增功能很糟糕:它们根本无法编译,并且它们散布了明显的剪切粘贴代码,如功能名不匹配(例如Int_Is_Valid / Pair_Is_Valid )所证明的Pair_Is_Valid

First, let's use a signature-package. 首先,让我们使用签名包。

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;

Given the rather obvious structuring of your code around access -types, I'm guessing that you are trying to "import" knowledge from C or C++. 考虑到您的代码围绕access类型的结构相当明显,我猜您正在尝试从C或C ++中“导入”知识。 That's going to cause you a lot of trouble/work later on if you try using Ada like it's a C-style language. 如果您尝试像A语言那样使用Ada,那将在以后给您带来很多麻烦/工作。

Also, it may be beneficial to sit down, take a breath, and think about things in terms of types both the problem-space and, separately, the architecture. 同样,坐下来,屏住呼吸并根据问题空间和体系结构的类型来思考问题可能是有益的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM