comp.lang.ada
 help / color / mirror / Atom feed
From: Niklas Holsti <niklas.holsti@tidorum.invalid>
Subject: Re: how to organize source code for a complete software? Thanks!
Date: Sun, 09 Oct 2011 19:48:34 +0200
Date: 2011-10-09T19:48:34+02:00	[thread overview]
Message-ID: <9fe53iFoe7U1@mid.individual.net> (raw)
In-Reply-To: <j6shle$h5f$1@dont-email.me>

On 11-10-09 18:20 , Jinsong Zhao wrote:
> Hi there,
>
> I am new to Ada, and I just have some experiences in using Fortran.

Welcome, then. May I ask what attracted you to Ada? I am asking partly 
out of curiosity, but also because if you tell us what you hope to gain 
by using Ada, we may better advise you on how to organize your code, and 
on other Ada usage.

> When using Fortran, I generally put each subroutine or function in a
> separate file, e.g., dot.for for dot product of two vectors. Then a
> main.for that call the necessary subroutine or functions. It seems to be
> rational.
>
> When I using Ada, I don't know how to do.

If you like, you can do exactly as you are accustomed to doing in 
Fortran (although this would be an unusual style for Ada). You can put 
each Ada function or procedure in its own compilation unit.

The only difference is that you will typically write two files per 
subprogram, one to specify the subprogram (its name, its parameters, and 
its return type, if it is a function) and one to implement the 
subprogram by giving its body, which contains the declarations of the 
local variables and the statements that are executed in the subprogram.

For example, let's make a function that adds two Float numbers and 
returns their sum, and let's call this function Add. In the following, I 
assume you are using the GNAT Ada compiler and its default conventions 
for source file names.

The Add function will have a specification (or declaration) file 
add.ads, which contains this one line:

    function Add (A, B : Float) return Float;

Any other subprogram that needs to use (call) the Add function will 
start with the line

    with Add;

which makes the compiler read the file add.ads, so that the compiler 
knows the name, parameters, and return type of Add.

The Add function will have an implementation (or body) file add.adb, 
which contains these five lines:

    function Add (A, B : Float) return Float
    is
    begin
       return A + B;
    end Add;

Assume that your main subprogram should use Add to compute the sum of 
the numbers 15.6 and 88.2 and print out the sum, and you give this 
procedure the name Main, then you would write it as the file main.adb 
with the following content:

    with Add;          -- So that we can call Add, below.
    with Ada.Text_IO;  -- So that we can call Put_Line, below.
    procedure Main
    is
    begin
       Ada.Text_IO.Put_Line (Float'Image (Add (15.6, 88.2)));
    end Main;

This program thus consists of three files: add.ads, add.adb, main.adb. 
To compile this program with GNAT, you only have to give the command

    gnatmake main

and GNAT will compile all three files and create an executable called main.

> There are procedure, function,
> package, package body, and so on (with different file extension, e.g.,
> ada, adb, ads, ...). And there is no main program.

The main program is just a parameterless procedure, like the Main above. 
It can be called anything you like; its name usually (but not 
necessarily) becomes the name of the program executable.

The one-subprogram-per-file method shown above, which is what you have 
been doing in Fortran, breaks down when you have subprograms that need 
to access some global data, for example like Fortran COMMON data. If you 
don't want to pass all this data in parameters, you must put it in a 
package. Then you can have two Ada designs, one that is very similar to 
Fortran COMMON, and the other that is more typical for Ada.

Here is how to do the Fortran-like design. Suppose that the Add 
function, above, should also include in the sum the value of a global 
variable Offset, that relates to some "scale" that the program is using, 
but you don't want to pass Offset as a parameter to Add. In Fortran, you 
could declare a COMMON area called Scale, containing a variable called 
Offset. In Ada, this can be implemented as a package Scale with a public 
variable Offset. Such a package is written in one file, scale.ads, as 
follows:

    package Scale
    is
       Offset : Float := 0.0;
    end Scale;

Here I also gave the Offset an initial value, just to avoid using an 
undefined value.

The Add function would now be written as follows, in add.adb:

    with Scale;
    function Add (A, B : Float) return Float
    is
    begin
       return A + B + Scale.Offset;
    end Add;

Note that:

- the line "with Scale" gives us access to the data in Scale, but

- you still have to use the qualified name, Scale.Offset, to refer
   to the variable Offset in the package Scale.

If you add a line after "with Scale", saying "use Scale", you can use 
just the name Offset instead of Scale.Offset. But many Ada programmers 
think that such "use clauses" should be used very sparingly, and that 
writing Scale.Offset is clearer.

Note that the file add.ads was not changed, since the text in that file 
does not have to refer to the Scale package.

Suppose now that your Main subprogram needs to Add the same two numbers 
as before, but with an Offset of 3.2. This would be written as follows, 
in main.adb:

    with Add;          -- So that we can call Add, below.
    with Scale;        -- So that we can access Scale.Offset, below.
    with Ada.Text_IO;  -- So that we can call Put_Line, below.
    procedure Main
    is
    begin
       Scale.Offset := 3.2;
       Ada.Text_IO.Put_Line (Float'Image (Add (15.6, 88.2));
    end Main;

> Would anyone here like to give a an analogy between the source code
> organization in Fortran and the one in Ada?

The example above was how one would represent Fortran COMMON in Ada 
packages, in a Fortran-like way: write the COMMON area as a package 
(*.ads file) with public variables representing the COMMON data, and 
then "with" this package in any subprogram (*.adb file) that needs to 
use this data.

However, this style is not the typical Ada style. The more common style 
in Ada is to make packages that contain both data and subprograms, and 
to make the data private as far as possible.

This is stretching the above example a bit, but the principle would be 
to put the Add function in the Scale package, since it needs to use 
Scale.Offset, and to move the Offset variable into the package body, so 
that it cannot be accessed from outside the package. The file scale.ads 
would now look like this:

    package Scale
    is
       function Add (A, B : Float) return Float;
       procedure Set_Offset (To : in Float);
    end Scale;

The declaration of the Offset variable, and the bodies of the two 
subprograms, would now be written in the "body file" for Scale, which is 
called scale.adb and contains this:

    package body Scale
    is

       Offset : Float := 0.0;

       function Add (A, B : Float) return Float
       is
       begin
          return A + B + Offset;
       end Add;

       procedure Set_Offset (To : in Float)
       is
       begin
          Offset := To;
       end Set_Offset;

    end Scale;

Of course, the files add.ads and add.adb are now unnecessary, since Add 
is both declared and implemented within package Scale, thus in the files 
scale.ads and scale.adb.

The Main subprogram in main.adb would now have this form:

    with Scale;        -- So that we can use it, below.
    with Ada.Text_IO;  -- So that we can call Put_Line, below.
    procedure Main
    is
    begin
       Scale.Set_Offset (3.2);
       Ada.Text_IO.Put_Line (Float'Image (Scale.Add (15.6, 88.2));
    end Main;

The single command "gnatmake main" is still enough to compile the 
program, although Main now uses other packages.

If you have any other subprograms that are also related to the Scale, 
and need to use Scale.Offset, you would put them in package Scale, too.

Of course, finding a good package structure can become complex if you 
have something like a Fortran program with several COMMON areas, and 
have several subprograms that use different subsets of the COMMON areas. 
You can fall back on the Fortran-like structure (one package per COMMON 
area, with public data), or look up some texts on "information hiding" 
and "software architecture", but even so, there are usually several ways 
to slice the cake, and the best way is often a matter of taste.

> And, please, if you like, point me a small, complete open source
> software in Ada (not a library or package). I should be compiled and
> run. I just hope to learn how to start a software in Ada by example.

Sorry, my internet access at present (on travel) is so wonky that I have 
no web access, in practice.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .



  parent reply	other threads:[~2011-10-09 17:48 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-10-09 16:20 how to organize source code for a complete software? Thanks! Jinsong Zhao
2011-10-09 17:16 ` stefan-lucks
2011-10-10  5:52   ` Jinsong Zhao
2011-10-09 17:37 ` Yannick Duchêne (Hibou57)
2011-10-10  6:02   ` Jinsong Zhao
2011-10-10 13:15     ` Paul Colin Gloster
2011-10-10 15:46       ` Simon Wright
2011-10-10 19:03       ` RasikaSrinivasan@gmail.com
2011-10-10 22:12       ` Randy Brukardt
2011-10-09 17:48 ` Niklas Holsti [this message]
2011-10-09 18:37   ` Ludovic Brenta
2011-10-09 21:24     ` J-P. Rosen
2011-10-10 12:09     ` Jinsong Zhao
2011-10-10  6:45   ` Jinsong Zhao
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox