I am trying to write a generic package and one of the operations required is to checksum the data records received over a bus. The record type will vary and it is a generic parameter. However any attempts to access the members of the generic parameter cause a compilation error.
The error ... (Ada 95 GNAT 2009)
file.adb:XX no selector "Data" for private type "The_Transfer_Type" defined at file.ads:YY
The declaration...
generic
type The_Transfer_Type is private;
SIZE : Integer;
package CC_Test_Channel is
function Checksum(Msg : The_Transfer_Type) return Integer;
end package
And the body ...
function Checksum(Msg : The_Transfer_Type) return Integer is
Sum : Integer := 0;
begin
-- calculate the checksum
for i in 1 .. SIZE loop
Sum := Sum + Integer(Msg.Data(i));
end loop;
return Sum;
end Checksum;
When you specify that a generic parameter is a private type, well, Ada assumes you mean it :-)
I.e. you have no access to its components. Ada is not "duck typed", so it's irrelevant whether or not you know that an instantiating type might actually possess a particular field. (How would you expect your Checksum function to work if The_Transfer_Type parameter were instantiated with, say, Integer?)
One way to address this is to also supply an accessor function as a parameter to the generic that will retrieve the data needed to, in this case, compute the checksum. E.g.:
generic
type The_Transfer_Type is private;
with function Get_Checksummable_Data_Item
(Msg : The_Transfer_Type;
I : Integer) return Integer;
SIZE : Integer;
package CC_Test_Channel is
function Checksum(Msg : The_Transfer_Type) return Integer;
end CC_Test_Channel;
The body is then:
function Checksum(Msg : The_Transfer_Type) return Integer is
Sum : Integer := 0;
begin
-- calculate the checksum
for i in 1 .. SIZE loop
Sum := Sum + Get_Checksummable_Data(Msg, I);
end loop;
return Sum;
end Checksum;
The function you supply for Get_Checksummable_Data is then specific to The_Transfer_Type and simply returns the selected value from The_Transfer_Type's component fields.
There are a number of other ways to set this up as well, like providing an unconstrained array type as a generic formal parameter and a formal function to retrieve it--this allows you to also get rid of the explicit SIZE formal parameter. Or you could write a Checksum() function as one of the operations on the type that you're instantiating CC_Test_Channel with, and then have:
with function Calculate_Checksum(Msg : The_Transfer_Type) return Integer;
as one of the generic formals.
Step back, and think about the possibilities...
(moved from a comment, as this got long)
Ada (95 and later) supports streams. Unlike C++ streams, which are pretty much for string conversion, Ada streams are meant as a general mechanism for performing operations on data (typically I/O).
Every Ada object has 'Write
and 'Read
attributes. There are some language supplied streams (for file I/O), but you also have the ability to create your own by deriving from Ada.Streams.Root_Stream_Type. If you write your own stream in this way, there are some low-level routines that give you direct access to the data.
This allows you to write your own streams to perform operations like I/O, data compression, or in your case perhaps checksumming data from the bus before loading it into variables (via 'Read). I've done it myself in the past to implement a record/playback functionality for our real-time software. I looked into it for compression once too (we ended up not needing the compression).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With