Search code examples
genericsadagnat

Is there a way to create and reuse aspect sets in Ada (specifically GNAT)?


In C/C++ I often use the preprocessor to define procedures that are based on common base procedure (yes, I know, functions in C).

For example (the names, types, and values are hypothetical):

// in some .h file
void some_base_procedure(int a, char c);

#define proc1(a) some_base_procedure(a, 'd')
#define proc2(a) some_base_procedure(a, 'e')
#define proc3(a) some_base_procedure(a, 'f')

I've looked at generics in Ada and have used them for packages, but for subprograms I'm not sure how to cleanly do something akin to the above C example.

I did come up with this however:

-- in some .ads file
procedure some_base(a:integer; c: character);

procedure proc1(a:integer; b: character := 'd') with
  Import    => True,
  Address   => some_base'Address;
procedure proc2(a:integer; b: character := 'e') with
  Import    => True,
  Address   => some_base'Address;
procedure proc3(a:integer; b: character := 'f') with
  Import    => True,
  Address   => some_base'Address;

This actually works fairly well, I only have to implement one body for some_base in the related .adb file, and I don't have to implement proc1, proc2, proc3 subprogram bodies that just call some_base with the correct parameter values. In some of my use cases though I have some more aspects then just Import and Address, so that might not scale well.

For lack of a better term I'll will refer to these as parameterized subprogram aliases.

A few problems with the above approach:

  1. One could still override b in a call to proc1, proc2 or proc3. This is minor, as that would have to intentionally be done (and for what purpose?)
  2. That it might be considered generally not an Ada way of doing things (might be considered a C'ism) and there might be cleaner way to do it with generics, but if it involves a separate ads/adb per subprogram, that is too verbose for such a simple use case as described above.
  3. If the aspects per parameterized subprogram alias becomes several lines, that is now more to have to update during maintenance, and becomes a similar maintenance issue as needing explicit bodies for each parameterized subprogram alias.

So my question is in regard to the last point above.

Is there some way of putting Import => True, Address => some_base'Address; into some sort of aspect set, and then reusing it for each parameterized subprogram alias?

So that it would be something like this (aspect_set, using some_base_set, ... made up for this example):

-- in some .ads file
procedure some_base(a:integer; c: character);
aspect_set some_base_set is Import    => True, Address   => some_base'Address;

procedure proc1(a:integer; b: character := 'd') using some_base_set;
procedure proc2(a:integer; b: character := 'e') using some_base_set;
procedure proc3(a:integer; b: character := 'f') using some_base_set;

Even if there is not, I consider my above approach good enough unless someone convincingly points out why it is a very bad approach, and that there is a more expressive Ada like way of doing something like this.


Solution

  • What's the problem you're having with generics?

    generic
       C : Character;
    procedure Some_Base(A : Integer);
    
    procedure Some_Base(A : Integer) is
    begin
       -- do something with A and B;
    end Some_Base;
    
    
    procedure Proc_1 is new Some_Base('d');
    procedure Proc_2 is new Some_Base('e');
    procedure Proc_3 is new Some_Base('f');