I am trying to understand tagged types and code extensions and reuse. I first create a program conventional_method_number_of_averages which implements one method for doing some calculation. Then I use extension of the tagged type and create a second program alternative_method_number_of_averages which implements a different method for solving the same problem. The difference is that now I have an additional parametercalled overlap_fraction. Here is the simple program with the first method conventional_method_number_of_averages that works fine:
-
number_of_averages.ads
with Conventional_Method_Number_Of_Averages; package Number_Of_Averages renames Conventional_Method_Number_Of_Averages; -
conventional_method_number_of_averages.ads
package Conventional_Method_Number_Of_Averages is type First_Method is tagged private; procedure Count_Averages (Any_Method : in out First_Method; Sampling_Frequency, FFT_Size, Time_Recorded: in Float); function Average_Is (Any_Method : in First_Method) return Float; private type First_Method is tagged record Number_Of_Averages : Float :=0.0; end record; end Conventional_Method_Number_Of_Averages; -
conventional_method_number_of_averages.adb
package body Conventional_Method_Number_Of_Averages is procedure Count_Averages (Any_Method : in out First_Method; Sampling_Frequency, FFT_Size, Time_Recorded: in Float) is begin Any_Method.Number_Of_Averages := (((Sampling_Frequency * Time_Recorded) - FFT_Size)*(2.0/FFT_Size)) + 1.0; end Count_Averages; function Average_Is (Any_Method : in First_Method) return Float is begin return Any_Method.Number_Of_Averages; end; end Conventional_Method_Number_Of_Averages; -
and the test file test_number_of_averages.adb
with Ada.Float_Text_IO; with Ada.Text_IO; use Ada.Text_IO; with Number_Of_Averages; with Conventional_Method_Number_Of_Averages; use type Number_Of_Averages.First_Method; procedure Test_Number_Of_Averages is Fs, Time_Duration, NFFT : Float := 0.0; Averages : Float := 0.0; Good_Method : Conventional_Method_Number_Of_Averages.First_Method; begin Ada.Text_IO.Put("Enter the sampling frequency "); Ada.Float_Text_IO.Get (Item => Fs); Ada.Text_IO.New_Line (1); Ada.Text_IO.Put("Enter the time recorded "); Ada.Float_Text_IO.Get (Item => Time_Duration); Ada.Text_IO.New_Line (1); Ada.Text_IO.Put("Enter the FFT size "); Ada.Float_Text_IO.Get (Item => NFFT); Ada.Text_IO.Put_Line (Ada.Text_IO.Get_Line); Ada.Text_IO.New_Line(1); Ada.Text_IO.Put("Number of averages = "); Number_Of_Averages.Count_Averages(Good_Method, Fs, NFFT, Time_Duration); Averages := Conventional_Method_Number_Of_Averages.Average_Is(Good_Method); Ada.Float_Text_IO.Put (Item => Averages, Fore => 3, Aft => 5, Exp => 0); end Test_Number_Of_Averages;
The above works fine as it is.
Now if I create an alternative method for the same calculation, I have:
-
its specification alternative_method_number_of_averages.ads
with Conventional_Method_Number_Of_Averages; use Conventional_Method_Number_Of_Averages; package Alternative_Method_Number_Of_Averages is type Second_Method is new First_Method with private; --override this function procedure Count_Averages (Any_Method : in out Second_Method; Sampling_Frequency, FFT_Size, Time_Recorded, Overlap_Fraction: in Float); private type Second_Method is new First_Method with record Overlap_Fraction : Float :=0.5; end record; end Alternative_Method_Number_Of_Averages;
and
-
its body alternative_method_number_of_averages.adb
package body Alternative_Method_Number_Of_Averages is --override this function procedure Count_Averages (Any_Method : in out Second_Method; Sampling_Frequency, FFT_Size, Time_Recorded, Overlap_Fraction: in Float) is begin Any_Method.Number_Of_Averages := ((Sampling_Frequency * Time_Recorded) - FFT_Size) / (FFT_Size - Overlap_Fraction * FFT_Size) + 1.0; end Count_Averages; end Alternative_Method_Number_Of_Averages;
Then on compiling the specification file, I get the error message:
alternative_method_number_of_averages.ads: no selector “number_of_averages” for type “Second_Method” defined at alternative_method_of_averages.ads. The culprit line is:
Any_Method.Number_Of_Averages := ((Sampling_Frequency * Time_Recorded) - FFT_Size) / (FFT_Size - Overlap_Fraction * FFT_Size) + 1.0;
So how to fix this?
In the end I would like to be able to have something like:
Number_Of_Averages.Count_Averages(Alternative_Method, Fs, NFFT, Time_Duration, Overlap_fraction);
in the test file test_number_of_averages.adb, similarly as the working code
Number_Of_Averages.Count_Averages(Good_Method, Fs, NFFT, Time_Duration);
Thanks a lot…
UPDATE
So now for the alternative method implementation, I have, renamed Alternative_Method_Number_Of_Averages.ads/adb to Conventional_Method_Number_Of_Averages-Alternative_Method_Number_Of_Averages.ads/adb. The test file is:
with Ada.Float_Text_IO;
with Ada.Text_IO; use Ada.Text_IO;
with Number_Of_Averages;
with Conventional_Method_Number_Of_Averages;
use type Number_Of_Averages.First_Method;
with Conventional_Method_Number_Of_Averages.Alternative_Method_Number_Of_Averages;
procedure Test_Number_Of_Averages is
Fs, Time_Duration, NFFT : Float := 0.0;
Averages1 : Float := 0.0;
Averages2 : Float := 0.0;
Good_Method : Conventional_Method_Number_Of_Averages.First_Method;
Alternative_Method : Conventional_Method_Number_Of_Averages.Alternative_Method_Number_Of_Averages.Second_Method;
begin
Ada.Text_IO.Put("Enter the sampling frequency ");
Ada.Float_Text_IO.Get (Item => Fs);
Ada.Text_IO.New_Line (1);
Ada.Text_IO.Put("Enter the time recorded ");
Ada.Float_Text_IO.Get (Item => Time_Duration);
Ada.Text_IO.New_Line (1);
Ada.Text_IO.Put("Enter the FFT size ");
Ada.Float_Text_IO.Get (Item => NFFT);
Ada.Text_IO.Put_Line (Ada.Text_IO.Get_Line);
Ada.Text_IO.New_Line(1);
Ada.Text_IO.Put("Number of averages = ");
Number_Of_Averages.Count_Averages(Good_Method, Fs, NFFT, Time_Duration);
Averages1 := Conventional_Method_Number_Of_Averages.Average_Is(Good_Method);
Ada.Float_Text_IO.Put (Item => Averages1, Fore => 3, Aft => 5, Exp => 0);
Ada.Text_IO.New_Line(1);
Ada.Text_IO.Put("Number of averages = ");
Number_Of_Averages.Alternative_Method_Number_Of_Averages.Count_Averages(Alternative_Method, Fs, NFFT, Time_Duration);
Averages2 := Conventional_Method_Number_Of_Averages.Alternative_Method_Number_Of_Averages.Average_Is(Alternative_Method);
Ada.Float_Text_IO.Put (Item => Averages2, Fore => 3, Aft => 5, Exp => 0);
end Test_Number_Of_Averages;
With Fs = 48000, Time_Duration = 60, NFFT = 8192, both methods are returning 702.125. The second method though is always returning 702.125 irrespective of the Overlap_Fraction parameter. How to specify Overlap_Fraction in the calling function, while still keeping using it private i.e. by having the function use the value of Overlap_Fraction stored in the record.
In the body of
Alternative_Method_Number_Of_Averagesyou are trying to useNumber_Of_Averages, which is a private member of the parent type. And since it is private,you don’t have visibility of it.
When you with a package you get visibility of the public part of the spec only.
A child package will have visibility of the public part of the parent in its own public part, and visibility of the private part of the parent in its own private part.
There’s also private childs, which have visibility of the whole parent in its own spec
To get visibility of
Number_Of_Averages, you can either move the full type declaration to the public part, or makeAlternative_Method_Number_Of_Averagesa child package ofConventional_Method_Number_Of_Averages.For more details, take a look at:
http://en.wikibooks.org/wiki/Ada_Programming/Packages
On another note, overriding occurs only when a type extension is implementing a subprogram with the same exact parameter profile as in the parent. In your example,
Second_Methodis overloadingCount_Averages, ie, adding a new subprogram with the same name but different profile.Count_Averagesis still inherited from the parent with the shorter parameter profile.