Search code examples
adaada95

How to use a generic type?


I'm working through the example here: https://www.adahome.com/rm95/rm9x-12-08.html

I've written my generic_stack.ads:

generic
    type Item_Type is private;
    size : Positive;

package Generic_Stack is
    procedure push( item : in Item_Type );

    function  pop return Item_Type;

    function  is_Empty return Boolean;

    STACK_EMPTY : exception;
    STACK_FULL  : exception;

end Generic_Stack;

And my generic_stack.adb:

package body Generic_Stack
is
    type Stack_Table is array (Positive range <>) of Item_Type;
    nodes : Stack_Table( 1..size );
    index : Natural := 0;

    procedure push( item : in Item_Type )
    is
    begin
        if ( index < size )
        then
            index := index + 1;
            nodes(index) := item;
        else
            raise STACK_FULL;
        end if;
    end push;

    function pop()
    return
        Item_Type
    is
        item : Item_Type;
    begin
        if ( index > 0 )
        then
            item := nodes( index );
            index := index - 1;
        else
            raise STACK_EMPTY;
        end if;
        return item;
    end pop;

    -- function is_Empty()   removed for the sake of brevity
end Generic_Stack;

I don't really understand how to actually use the Generic_Stack.
With the simple generic_stack_test.adb code:

with Generic_Stack;
package Stack_Int_Type is new Generic_Stack( Item_Type => Integer, Size => 32 );

procedure Generic_Stack_Test
is
    stack : Stack_Int_Type;
begin
    stack.push( 3 );
end Generic_Stack_Test;

Gnat gives me errors on compilation:

# gnat make -gnat95 generic_stack_test.adb -o generic_stack_test
x86_64-linux-gnu-gcc-8 -c -gnat95 generic_stack_test.adb
generic_stack_test.adb:9:08: keyword "body" expected here [see file name]
generic_stack_test.adb:20:24: missing "end Stack_Int_Type;"
x86_64-linux-gnu-gnatmake-8: "generic_stack_test.adb" compilation error

Do I have to declare the Stack_Int_Type or suchlike? I don't understand how to use a declare inside a procedure. If I pass a Stack_Int_Type to another procedure, does it have to declare the type too?

Is it possible to simply declare Stack_Int_Type once in an .ads, and use it as a regular type? My book and web-pages kind of suggest it has to be declared every time, which sounds onerous.


Solution

  • Your test code is actually two library items:

    with Generic_Stack;
    package Stack_Int_Type is new Generic_Stack( Item_Type => Integer, Size => 32 );
    

    declares a library package Stack_Int_Type, and

    procedure Generic_Stack_Test
    is
        stack : Stack_Int_Type;
    begin
        stack.push( 3 );
    end Generic_Stack_Test;
    

    declares a library procedure which, as it stands, knows nothing about Stack_Int_Type.
    We can fix that by adding the necessary with, but (compiling with -gnatl)

     1. with Stack_Int_Type;
     2. procedure Generic_Stack_Test
     3. is
     4.    stack : Stack_Int_Type;
                   |
        >>> subtype mark required in this context
        >>> found "Stack_Int_Type" declared at stack_int_type.ads:2
    
     5. begin
     6.    stack.push( 3 );
           1    2
        >>> invalid prefix in selected component "stack"
        >>> prefixed call is only allowed for objects of a tagged type
    
     7. end Generic_Stack_Test;
    

    What’s happening here is that Generic_Stack doesn’t declare a type, so you can’t declare an instance of it at line 4; it’s a sort of singleton. (Amongst other things, that means it’s confusingly named: I’d’ve called it Integer_Stack. Never call a package _Type; _Types, maybe.) Fixing that,

    with Generic_Stack;
    package Integer_Stack is new Generic_Stack( Item_Type => Integer, Size => 32 );
    

    and

    with Integer_Stack;
    procedure Generic_Stack_Test
    is
    begin
       Integer_Stack.push( 3 );
    end Generic_Stack_Test;
    

    You could have made Integer_Stack local:

    with Generic_Stack;
    procedure Generic_Stack_Test
    is
       package Integer_Stack
       is new Generic_Stack( Item_Type => Integer, Size => 32 );
    begin
       Integer_Stack.push( 3 );
    end Generic_Stack_Test;