tags:

views:

164

answers:

3

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;
+3  A: 

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...

Marc C
As long as he is stepping back, it might be worth considering using the stream operations to get at the data he wants...
T.E.D.
This was the solution I went with.What are the stream operations?
mat_geek
The answer got kind of long, and I decided it needed formatting, so I moved it into a second answer below.
T.E.D.
+3  A: 
generic 
  type The_Transfer_Type is private; 
  ...

The above code means that the client can supply any type they dream up for The_Transfer_Type (as long as it isn't "limited"). It also means that your generic knows absolutely nothing about the type, except that asignment is available.

With Ada generics there is sort of an inverse relationship between how many different kinds of objects can be supplied for a generic parameter, and what operations are avilable to the generic on those objects. For instance, the most open type would be is limited private. You could supply any type whatsover for one of those. However, the generic can do almost nothing with it. Even assignment wouldn't be available.

Take away the "limited" and you can do asignments with it, but only types that can be assigned may be supplied. On the other extreme, you could define it as: type The_Transfer_Type is (<>) and then you could supply any integer or enumerated type, and would gain things like 'first. Going still further, you could do type The_Transfer_Type is range <>, and you'd gain the ability to do integer math operations, but would only be able to supply integer numeric types.

T.E.D.
+1  A: 

(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 mechanisim 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 funtionality for our real-time software. I looked into it for compression once too (we ended up not needing the compression).

T.E.D.