How to use a generic type?

287 Views Asked by At

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.

3

There are 3 best solutions below

1
On BEST ANSWER

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;
0
On

As explained elsewhere, no type is defined by your package specification. Otherwise, you would have type ... is somewhere after the package keyword. It's that simple.

But as explained elsewhere too, it's not dramatic. Your package instantiation will define exactly one stack, and not a type to be used in multiple places. In some cases, it's exactly what you need. So, your could call your instantiated package My_Stack, which actually articulates around an object (My_Stack.nodes, accessible only through Push & Pop).

You need to do the instantiation of Generic_Stack from within a unit (procedure, package, ...). Outside of it, it is the "outer space" only with with and use clauses needed to connect with other units.

with Generic_Stack;

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

Your Generic_Stack package never defines a stack data type. The procedure Push and Pop are operations on a stack. You must have a type upon which to operate. There are two general categories of stacks; bounded stacks and unbounded stacks. You must decide which kind of stack you wish to create. A discussion of both kinds of stacks implemented in Ada can be found at https://sworthodoxy.blogspot.com/2019/02/stack-abstract-data-type-using-ada.html

In the examples referenced in the URL above you will see how to create stack types that can be used multiple times. The example from Adahome is an old and problematic example. You have identified the biggest problem.