Inspirel banner

Programming Distributed Systems with YAMI4

10.4.1 Ada

The Ada language mapping is quite straightforward, as most of the YAMI4 Definition Language syntax was inspired by analogous elements of Ada.

Packages are mapped directly with similar consequences to the visibility of names in importing and parent-child relations of packages. Both package specifications and package bodies are generated with the structure that mirrors the packaging structure of YDL sources. In particular, package imports are mapped to the with clause and parent-child package relationships are retained.

Types from YDL are mapped to record types in Ada.

Type fields are mapped to record fields.

Field types are mapped to standard Ada types or to types from the YAMI.Parameters package in the following way:

Optional fields are mapped to pairs of fields, where the normally generated field is accompanied by the Boolean field named Field_Valid that allows to express whether the given field is valid or not.

Each generated record type has two primitive operations, Write and Read, which can be used to automate translation of the given record type to and from Parameters_Collection.

For example, the following type description in YDL (taken from the calculator example):

   type Results is
      Sum : Integer;
      Difference : Integer;
      Product : Integer;
      Ratio : optional Integer;
   end Results;

is mapped to the following Ada specification:

   type Results is record
      Sum : YAMI.Parameters.YAMI_Integer;
      Difference : YAMI.Parameters.YAMI_Integer;
      Product : YAMI.Parameters.YAMI_Integer;
      Ratio_Valid : Boolean := False;
      Ratio : YAMI.Parameters.YAMI_Integer;
   end record;

   procedure Write (V_Y4 : in Results;
      P_Y4 : in out YAMI.Parameters.Parameters_Collection);
   procedure Read (V_Y4 : out Results;
      P_Y4 : in YAMI.Parameters.Parameters_Collection);

and the following code in respective package body:

   procedure Write (V_Y4 : in Results;
      P_Y4 : in out YAMI.Parameters.Parameters_Collection) is
   begin
      P_Y4.Set_Integer ("sum", V_Y4.Sum);
      P_Y4.Set_Integer ("difference", V_Y4.Difference);
      P_Y4.Set_Integer ("product", V_Y4.Product);
      if V_Y4.Ratio_Valid then
         P_Y4.Set_Integer ("ratio", V_Y4.Ratio);
      end if;
   end Write;

   procedure Read (V_Y4 : out Results;
      P_Y4 : in YAMI.Parameters.Parameters_Collection) is
   begin
      V_Y4.Sum := P_Y4.Get_Integer ("sum");
      V_Y4.Difference := P_Y4.Get_Integer ("difference");
      V_Y4.Product := P_Y4.Get_Integer ("product");
      declare
         E_Y4 : YAMI.Parameters.Parameter_Entry;
      begin
         P_Y4.Find ("ratio", E_Y4, V_Y4.Ratio_Valid);
         if V_Y4.Ratio_Valid then
            V_Y4.Ratio := P_Y4.Get_Integer ("ratio");
         end if;
      end;
   end Read;

As can be seen, optional fields allow to work with parameters entries that might or might not exist at all. Record types that are generated for user-defined types are used on both client- and server-side.

It should be noted that these operations do not clear the target object before performing the translation - this means that they are working in the incremental way, which can provide potential flexibility.

Interfaces, on the client-side, are mapped to tagged record types with the same name that encapsulate the connection details of the remote object - the agent object in use, the server location, object name and requested operation timeout. These details are left in the public part of the generated code just in case they are needed for diagnostic purposes, but are generally not relevant in regular use. For the calculator example, the Operations interface is mapped to the following tagged type:

   type Operations is tagged record
      Agent : YAMI.Agents.Agent_Access;
      Server_Location : Unbounded_String;
      Object_Name : Unbounded_String;
      Timeout : Duration;
   end record;

This tagged record type has the Initialize_Interface operation that makes it easier to associate it with the agent object that is intended for handling physical communication. In this example, the interface named Operations, has the following initialization procedure:

   procedure Initialize_Operations
     (Client_Y4 : out Operations;
      Agent : in out YAMI.Agents.Agent;
      Server_Location : in String;
      Object_Name : in String;
      Timeout : in Duration := 0.0);

This tagged type offers additional primitive operations, one for each message declared in the interface. This operation encapsulates the complete interaction with remote object and takes care of parameter translation as well. In the case of calculator example, the generated Calculate operation has the following signature:

   procedure Calculate (Client_Y4 : in out Operations;
      Op : in Operands; Res : out Results);

As can be seen above, the user-defined types and their respective record type in generated code are used in this signature in a natural way.

The run-time implementation of this operation involves the following sequence of actions:

This sequence of actions allows to provide the remote procedure call functionality.

As a counterpart of these definitions, the generated code contains also an abstract type with primitive operations for use on the server-side. This type is intended to be a base type for user-defined extensions that provide the actual implementation for all message operations.

The Operations interface is mapped to the following server-side abstract type in Ada:

   type Operations_Server is abstract
      new YAMI.Incoming_Messages.Message_Handler with null record;

The specializations of this type can be directly registered as YAMI4 objects thanks to the Message_Handler interface.

For each message in the given interface, a separate abstract procedure is generated:

   procedure Calculate (Server_Y4 : in out Operations_Server;
      Op : in Operands; Res : out Results) is abstract;

Server programmers are expected to implement this procedure in their derived type according to the application needs. In the case of the calculator example, the implementation performs all four calculations on the given operands and puts the results in the output parameter.