Master_T
Master_T

Reputation: 7913

Ada: is there a proper way for two types to "use" one another? Limited with + access = can't compile

I'm fairly new to Ada, and I'm having endless problems with types, references and inclusions. Below I have two types that need to "use" each other: Train and Platform, I included the source code below.

Since a direct "withing" will not work (I understand two ada types can't "see" each other directly, or a circular inclusion error will occur), I opted to have a common package (also included below) that "limited with"s the other two, and also implements access types so that including common will enable a package to use pointers to Train and Platform.

However it seems that this is not a feasible solution either: while I can declare variables using the ref types defined in common I cannot actually access any method/entry on them. I highlighted the troublesome line in Railway-train.adb with a comment. When the compiler reaches that line it stops with error:

Invalid prefix in selected component "StopAtPlatform"

I know that Ada is very different from other languages, but as someone that comes from C# I really have problems understanding why it is so hard to have two types using each other and why thos "pointers" (access types) seem not to be working properly. Can anyone explain what I am doing wrong?

Please, if you can, refrain from any comment such as "why do you need to have two types use each other, that's a bad practise"... thanks :)

Source follows:

Railway-train.ads

with Railway.Common; use Railway.Common;
with Ada.Text_IO; use Ada.Text_IO;

package Railway.Train is

   task type Train is
      entry Create(id : in Natural; capacity : in Positive; Route : Route_Array; me : Train_Ref);
   end Train;

end Railway.Train;

Railway-train.adb:

package body Railway.Train is

   task body Train is
      Myself : Train_Ref;
      Train_ID : Natural;
      Passenger_Load : Integer := 0;
      Passenger_Capacity : Positive;
      Train_Route : Route_Array;
   begin
      accept Create (id : in Natural; capacity : in Positive; Route : in Route_Array; me : Train_Ref) do
         Myself := me;
         Train_ID := id;
         Passenger_Capacity := capacity;
         Train_Route := Route;
      end Create;

      loop
         for i in Train_Route'Range loop
            Train_Route(i).StopAtPlatform(Myself); --ERROR OCCOURS HERE
         end loop;
      end loop;
   end Train;

end Railway.Train;

Railway-platform.ads:

with Railway.Common; use Railway.Common;

package Railway.Platform is

   protected type Platform is
      entry StopAtPlatform(Incoming_Train : in Train_Ref);
      procedure DepartFromPlatform;
   private
      Train_At_Platform : Train_Ref := null;
   end Platform;

end Railway.Platform;

railway-platform.adb:

package body Railway.Platform is

   protected body Platform is

      --Train occupies the platform. This stops the access to the platform by all other trains, until DepartFromPlatform is called
      entry StopAtPlatform(Incoming_Train : in Train_Ref) when Train_At_Platform = null is
      begin
         Train_At_Platform := Incoming_Train;
      end StopAtPlatform;

      --Train leaves the platform. This re-opens access to the platform by all other trains
      procedure DepartFromPlatform is
      begin
         Train_At_Platform := null;
      end DepartFromPlatform;

   end Platform;

end Railway.Platform;

And finally, Railway-common.ads:

limited with Railway.Platform;
limited with Railway.Train;

package Railway.Common is

   --Common stuff
   type Train_Ref is access all Train.Train;
   type Platform_Ref is access all Platform.Platform;

   type Route_Range is range 1..100;
   type Route_Array is array (Route_Range) of Platform_Ref;

end Railway.Common;

EDIT: as ajb's answer below points out, adding with Railway.Platform; on top of Railway-train.adb generates a compiler bug :(

Upvotes: 1

Views: 282

Answers (2)

Jacob Sparre Andersen
Jacob Sparre Andersen

Reputation: 6611

One solution is to declare both types in the same package.

Upvotes: 0

ajb
ajb

Reputation: 31699

I think the correct answer is that you need

with Railway.Platform;

above package body Railway.Train;. The reason is that Railway.Common's specification defines Route_Array as an array of Platform_Ref, which is an access to a limited-view type (because Railway.Common says limited with Railway.Train and Platform). limited with does not make detailed information about the types in Railway.Platform available, which is why StopAtPlatform isn't visible. Adding a regular with to Railway.Train's body makes that identifier visible. It does not create a circular dependency, since it's on the body of Railway.Train.

However, when I tried this using GNAT, the result was a bug box. I assume that's what you're using, since it gave me the exact same error message before I added the with. So while I think this answer is correct, it may not help you until they can fix the problem.

I think the main reason why this is harder in Ada than C# is that Ada is an older language and was originally designed for the paradigm in which when you compile a file, the compiler must have all the information it needs. A source file that referred to an identifier that it hadn't yet seen (because it was in a source file that hadn't yet been compiled) wouldn't work. I think it's fair to say that the compiler technology available at the time just wasn't there yet.

EDIT: My earlier version made some misstatements about the structure of the program.

Upvotes: 1

Related Questions