2

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.

bogen85
  • 83
  • 6

2 Answers2

4

After looking into this more, I found a more general solution.

-- in some .ads file
procedure some_base(a:integer; c: character);
procedure proc1(a:integer; b: character := 'd') renames some_base;
procedure proc2(a:integer; b: character := 'e') renames some_base;
procedure proc3(a:integer; b: character := 'f') renames some_base;

This is cleaner than what I was using, even though one could still override the defaults, but that is minor. It addresses the maintenance concerns, which was the whole point of the question in the first place.

bogen85
  • 83
  • 6
3

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');
egilhh
  • 6,464
  • 1
  • 18
  • 19
  • Based on your answer I just now tried something like that. Inside an .adb file maybe I can do that? But I can't put the body of some_base in an .adb file, an the generic prototype in the .ads file, without getting warning: cannot instantiate "Some_Base" before body seen. I want all the derived procedures to be defined in the same .ads file. – bogen85 Sep 12 '20 at 20:20
  • 1
    Some_Base can be in its own .ads and .adb – egilhh Sep 12 '20 at 20:24
  • I can get generics to work as you say, I'm just not getting them to work with my use case, where the final derived functions are setting other defaults (such as when using GNAT.Source_Info, which works with the "renames" method. – bogen85 Sep 12 '20 at 21:18
  • 1
    Well, that would work by just using Some_Base... no need to rename, instantiate nor Import – egilhh Sep 12 '20 at 21:51
  • I got it working. Using generic function added some complexity to the package hierarchy, but it is fine. before I just had package1.ads and package1.adb. Now I have package1.ads, package2.ads, base_procedure.ads, base_procedure.adb. base_procedure uses package1, package2 uses base_procedure. So a bit odd in that regard, but I got it working. – bogen85 Sep 12 '20 at 23:18
  • It does work with the GNAT.Source_Info functions. (where the calls are ultimately invoked). – bogen85 Sep 12 '20 at 23:24