❌ About FreshRSS

Normal view

There are new articles available, click to refresh the page.
Before yesterdayAda – The Craft of Coding

Coding Ada: Using an exception

By: spqr
27 March 2018 at 14:01

Some people wonder where you can use a simple exception in Ada. Functions are an ideal place. Below is a program which contains a function isNumber() which returns true if the string passed to it is considered a number (integer). So 1984 us a number, but x24 is not. The program takes a word as input from the user (as an unbounded_string), and passes it to isNumber(). The function isNumber() converts the unbounded_string to a string, and then to an integer using integer’value(). If the conversion works, then the function returns true. If however, the conversion fails, then a Constraint_Error exception will be triggered, and false will be returned.

with ada.Text_IO; use Ada.Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with ada.strings.unbounded; use ada.strings.unbounded;
with ada.strings.unbounded.Text_IO; use ada.strings.unbounded.Text_IO;

procedure number is

   aWord : unbounded_string;
   num: integer;
 
   function isNumber(s: unbounded_string) return boolean is 
      temp: integer;
   begin
      temp := integer'value(to_String(s));
      return true;
      exception 
         when Constraint_Error => return false;
   end isNumber;

begin
   put_line("Enter a word: ");
   get_line(aWord);
 
   if (isNumber(aWord)) then
      num := integer'value(to_String(aWord));
      put(num);
   else
      put_line("Not a number");
   end if;
end number;

This sort of code could be used to count numbers in a document or something similar. You can use float’value to do the same for floating-point numbers.

spqr

Coding Ada: reading lines from files.

By: spqr
4 April 2018 at 22:03

How to read lines from a text file in Ada?

First create some variables for the filename, string to hold the filename, a string to hold the line of text, and a boolean value to hold the file status (nameOK):

infp : file_type;
sline : unbounded_string;
fname : unbounded_string;
nameOK : boolean := false;

Then read in the filename, continuously if it does not exist, or there are other issues with it. When this is okay, open the file for reading, and loop through the file, using the function get_line() to read a line of text, and store it in the unbounded_string sline. This can then be further processed, before a new line of text read.

put_line ("Enter the filename: ");
loop
   exit when nameOK;
   get_line(fname);
   nameOK := exists(to_string(fname));
end loop;

open(infp, in_file, to_string(fname));

loop
   exit when end_of_file(infp);
   -- Process each line from the file
   get_line(infp,sline);
   -- do something with the line of text
end loop;


 

 

 

spqr

💾

Ada and goto

By: spqr
1 March 2019 at 16:10

Every language has its hidden “features”, and Ada, like most other languages has a goto statement. Any statement could be labelled by an identifier enclosed in double angle brackets, << >>. For example:

<<gohere>> g = 12.3; ...
goto gohere;

However, the goto in Ada is somewhat better behaved. It cannot transfer control outside the current subprogram or package body; it cannot transfer control inside a structure (e.g. from else to then in an if statement); and it cannot transfer control from the outside of a structured statement into the body of a structured statement. Consider the following code:

with ada.Text_IO; use Ada.Text_IO;
with ada.Integer_Text_IO; use ada.Integer_Text_IO;

procedure gotoada is
   x,y,z : integer;
 
begin
         put_line("Enter a number: ");
         get(x);
<<lbl1>> if x = 1 then
            y := 1;
            z := 1;
<<lbl2>> else
            y := 2;
            z := 2;
         end if;

         case y is
            when 1 =>
               put_line("jump to top");
               goto lbl1;
            when others =>
               put_line("jump to middle");
               goto lbl2;
         end case;

end gotoada;

The goto associated with lbl1 is allowed. However the goto associated with lbl2, which jumps into the middle of the if statement above it,  is not allowed. Try to compile this, and you’ll get the following compiler message:

gotoada.adb:24:21: target of goto statement is not reachable

Languages like Pascal would easily allow this sort of code, but Ada was designed to prevent issues with uber unstructured code.

 

spqr

💾

Coding Ada: Timing code and passing functions to functions

By: spqr
19 March 2019 at 14:03

If you have to time a function in a language like Ada, one option is to create a timer function that takes the function to be timed as a parameter. Here is an Ada program that times the Ackermann function. There is nothing special about the ackermann() function, except that it is recursive. The interesting part of the code involves creating a type, functype, to hold the function “pointer”, or access point. The type uses the phrase “access function” followed by the parameter list for the function in question, and the type of the return value. This denotes an Access type, which is the equivalent of a pointer in other languages. Rather than using the term “points to”, Ada prefers to refer to this type of entity as “granting access” to an object. An access to subprogram allows the caller to call a subprogram without knowing its name nor its declaration location.

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

procedure passingfunc is

   type functype is access function (x,y: integer) return integer;

   function ackermann(m,n: in integer) return integer is
   begin
      if m = 0 then
         return n+1;
      elsif n = 0 then
         return ackermann(m-1,1);
      else
         return ackermann(m-1,ackermann(m,n-1));
      end if;
   end ackermann;

   procedure timeit(Func : functype; m,n : in integer) is 
      result : integer;
      startTime, endTime : time;
      milliS : Duration;
   begin
      startTime := Clock;
      result := Func(m,n);
      endTime := Clock;
      milliS := (endTime - startTime) * 1000;
      put_line("Result: " & Integer'Image(result));
      put_line("Runtime = " & Duration'Image(milliS) & " milliseconds.");
   end timeit;

begin
   timeit(ackermann'Access, 4, 1);
end passingfunc;

The function timeit() can then be implemented. It declares Func as one of the parameters, implying that a function (pointer) can be passed to it. The remaining two parameters are the two parameters to be passed onto ackermann(). Note that the call to Func() is quite normal as well – no magic needed. The guts of the timing algorithm are described in another post, so the interested reader is referred there. When the function timeit() is called in the main program, the ackermann() function is passed using ackermann’Access.

Here’s the program running… with some trivial output:

Result: 65533
Runtime = 17441.122000000 milliseconds.

Those of you who have ever implemented a non-recursive version of Ackermann will note the runtime of 17-odd seconds, which is much faster than the 60-80 seconds of the version using a stack.

spqr

Coding Ada: Bitwise operators

By: spqr
7 April 2020 at 02:14

In Ada, you can find similar bitwise operators to other languages: and, or, xor, and operators that shift left and right. The trick is that they use modular types. These types are unsigned and have “wrap-around” semantics. Consider the following declaration:

subtype byte is unsigned_8;
package byteIO is new ada.text_io.modular_io(byte);
x, y, z: byte;

This defines an integer type whose values range from 0 to 255 (on most processors), i.e. the integers that will fit in a byte. Since it’s “modular”, it means that arithmetic operations on the type wrap around (i.e. if you add 130 + 130, you will get 4. With this declaration, you can create binary numbers:

x := 2#00011110#;
y := 2#11110100#;
z := 2#11110000#;

Here x=30, y=244, and z=240. So we can then use bitwise operators (found in the Ada package Interfaces) to swap the numbers:

x := x xor y;
y := y xor x;
x := x xor y;

The numbers can be printed out with this code:

put_line(unsigned_8'image(x));
put_line(unsigned_8'image(y));

You can also use put from the sub-package byteIO:

byteIO.put(item => z, base => 2);

If we want to convert a number input from integer to type byte, we can do so in the following manner:

w: integer;
get(w);
z := byte'val(integer'pos(w));
byteIO.put(item => z, base => 2);

The byte’val returns the base type of byte. For example, if the value input by the user is 17 (w), then the value assigned to z will be 10001. What about shift operators? There is shift_left() and shift_right(). Here’s an example:

zs: byte; 
zs := shift_left(z,1);
byteIO.put(item => zs, base => 2);

If the user inputs 12, then the value of zs will be 24, as it has been shifted left 1 bit.

spqr

Programming made easy – loops (i)

By: spqr
23 April 2020 at 19:53

Humans are, by nature, repetitive beings. Watch the Star Trek: TNG episode “Cause and Effect”, and you will quickly understand what a loop is as the Enterprise is caught in a causality time loop. There are also times when programs must do a task more than once. If the repetition is limited to some countable number of recurrences with a foreseeable end then it is called a loop. And if, at least in the perception of the individual who is experiencing a certain situation, there is no end in sight then one is talking about endless or infinite loops. All of the above can be desirable or not. Here is a visual illustration:

The code in the program moves linearly from statement A, until it encounters a loop. At the start of the loop, the loop control tests some condition. If the condition is true, then the loop activates the piece of code (made up of one or more statements) in what is called the loop body. When that code is done the loop returns to the loop control, and the process starts all over. If the condition in the loop control becomes false, nothing happens, and the program ends the loop, and continues on to statement B. There is also a possibility that the first time into the loop, the condition becomes false, and in that case the loop is bypassed.

Loops are structures that allow for repetition – repeating an activity a number of times. There are different kinds of repetition:

  1. Loops where the number of repetitions is known before the loop activates.
  2. Loops where the number of repetitions isn’t known, and is controlled by some condition.
  3. Loops that are infinite, i.e. could continue to infinity and beyond.

The best example of the first case encountered in most languages is the for loop. Here we use a variable called a loop index to control the loop. Each time a loop loops it is called an iteration. So if the loop index is x, and it repeats 100 times, then x will begin with a value of 1, and increment by 1 each iteration, until x reaches 100. In Julia this is easily achieved in the following manner (in this case the loop simply prints out each value of x):

for x = 1:100
   println(x)
end

In Ada it takes a similar format (the printing part is a little different, but just ignore that bit):

for x in 1..100 loop
    put(x); 
    new_line;
end loop;

Ada uses the keyword loop to signify it as a loop. Fortran also uses a similar construct, but has never used the for loop, opting instead for the keyword do. Same concept, different name.

do x = 1, 100
   write(*,*) x
end do

C also uses a for loop, although it can be somewhat more convoluted:

for (x=1; x<=100; x=x+1)
   printf("%d\n",x);

All have their varied syntax, but achieve the same goal – a known number of iterations.

spqr

Programming made easy – loops (ii)

By: spqr
24 April 2020 at 14:39

With the basics of for loops, let’s look at an example of an actual program which calculates the Harmonic series, which is an infinite series of the form:

h(n) = 1 + 1/2 + 1/3 + … + 1/n

The code for the program in Fortran, Ada, Julia and C is shown below. The loop is highlighted in blue.  For interest sake, the loops are presented in reverse, i.e. 1/n + 1/(n-1) + … + 1, as it illustrates clearly how each language deals with the simple issue of a decreasing index. In each case the starting value of the index variable i is n, and the ending value is 1. Here it is the algorithm for the Harmonic series depicted visually, clearly showing the role of the loop:

Fortran

program harmonic
   integer :: n, i
   real :: h

   read (*,*) n
   h = 0
   do i = n,1,-1
      h = h + 1.0/i
   end do
   write(*,*) h
end program harmonic

Here the third item (the modifier) in the Fortran loop denotes the type of change to the loop index, in this case, a decrease by 1. In a  normal loop incrementing by 1, the modifier can be omitted.

C

#include <stdio.h>

int main(void){
   int i, n;
   float h;

   scanf("%d", &n);
   printf("%d\n", n);
   h = 0.0;
   for (i=n; i>=1; i=i-1)
      h = h + 1.0/i;
   printf("%lf\n", h);
   return 0;
}

In the C version, the loop index is decremented using the statement i=i–1. For more than one statement after the for loop definition, the statements would have to be encapsulated in { and }.

Ada

with ada.Float_Text_IO; use Ada.Float_Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure harmonic is
   n : integer;
   h : float;
begin
   get(n);
   h := 0.0;
   for i in reverse 1..n loop
      h := h + 1.0/float(i);
   end loop;
   put(h);
end harmonic;

In Ada, the keyword reverse is used to specify a loop will be decreasing in value.

Julia

n = parse(Int64, chomp(readline()))
println(n)
h = 0
for i = n:-1:1
   h = h + 1.0/i
end
println(h)

Here the index modifier is placed in the centre, n:-1:1, implying i has the values n to 1, modified by -1 each loop iteration. In a  normal loop incrementing by 1, the modifier can be omitted.

spqr

Programming made easy – loops (iii)

By: spqr
27 April 2020 at 13:44

There are of course other loops. These loops are normally used when the number of times a loop iterates isn’t known. These type of loops generally use some form of condition to control the loop. The most prominent type of loop is the while loop. A while loop checks a condition, and if true, processes the code in the loop body, otherwise it exists the loop. Unlike the for loop, which controls the values of the loop index in terms of updating its value, in the loops like while, this process must be added, i.e. the value of i is given the value 1, before the loop is activated, and its value is incremented within the loop body.

We will consider some of these loops using the same Harmonic series, this time in increasing order. The code for the program in Fortran, Ada, Julia and C is shown below. The loop is highlighted in blue.

Fortran

program harmonic
   integer :: n, i
   real :: h

   read (*,*) n
   h = 0
   i = 1
   do while (i <= n)
      h = h + 1.0/i
      i = i + 1
   end do
   write(*,*) h
end program harmonic

Here the index variable, i, must be initiated to the value 1 before the loop begins, and incremented within the loop. The condition states that the loop continues while the value of i is less than or equal to n, i.e. (i <= n) . Fortran also provides a more generic do/end do loop.

C

#include <stdio.h>

int main(void){
   int i, n;
   float h;

   scanf("%d", &n);
   printf("%d\n", n);
   h = 0.0;
   i = 1;
   while (i <= n){
      h = h + 1.0/i;
      i = i + 1;
   }
   printf("%lf\n", h);
   return 0;
}

In the C version, the structure is almost the same, except that the do while of Fortran has been replace with a simple while. With the addition of the statement to increment the value of i, the loop “contents” are now enclosed within { and }. C also has another loop, the do/while() loop, that performs the loop once, then evaluates the condition at the end.

Ada

with ada.Float_Text_IO; use Ada.Float_Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure harmonic is
   n, i : integer;
   h : float;
begin
   get(n);
   h := 0.0;
   i := 1;
   while i <= n loop
      h := h + 1.0/float(i);
      i := i + 1;
   end loop;
   put(h);
end harmonic;

In Ada, the while loop looks similar to that of the other to Fortran and C, it is just subtleties in syntax that set them apart. Here there are no parenthesis around the condition. Also note that in the for loop, the index variable, i, did not have to be declared, but here it does. Ada also provides a generic loop/end loop loop.

Julia

n = parse(Int64, chomp(readline()))
println(n)
h = 0
i = 1
while i <= n
   h = h + 1.0/i
   i = i + 1
end
println(h)

Same deal here. Similar to Ada, except the loop keyword after the condition isn’t needed. There are only two loops in Julia.

As you can see, over four languages, the only thing that fundamentally changes is the basic syntax of the loop.

spqr

Programming made easy – nested loops

By: spqr
1 May 2020 at 13:21

We’re not quite done with loops yet. There are still a few things to learn, although some can be left for later. There are certain situations where it is appropriate to use a loop inside another loop. Such a loop is called a nested loop. A nested loop is executed, essentially, from the inside out. Each loop is like a layer and has its own loop control, its own condition and its own loop body. Below is a schematic of three nested loops.

A loop inside a loop, inside a loop.

There are many different kinds of nested loops, and they are largely dependent on the underlying algorithm. Below are some examples of the types of nested loops that are possible.

Various examples of nested loops

We will consider how a nested loop can be used to create a times table. It basically works by iterating the inner loop 12 times for every iteration of the outer loop. So, 12 × 12 = 144 iterations. The outer loop is highlighted in blue, and the inner loop in red. Here is a sample output:

 1   2   3   4   5   6   7   8   9  10  11  12
 2   4   6   8  10  12  14  16  18  20  22  24
 3   6   9  12  15  18  21  24  27  30  33  36
 4   8  12  16  20  24  28  32  36  40  44  48
 5  10  15  20  25  30  35  40  45  50  55  60
 6  12  18  24  30  36  42  48  54  60  66  72
 7  14  21  28  35  42  49  56  63  70  77  84
 8  16  24  32  40  48  56  64  72  80  88  96
 9  18  27  36  45  54  63  72  81  90  99 108
10  20  30  40  50  60  70  80  90 100 110 120
11  22  33  44  55  66  77  88  99 110 121 132
12  24  36  48  60  72  84  96 108 120 132 144

Ada

with ada.Text_IO; use Ada.Text_IO;
with ada.Integer_Text_IO; use ada.Integer_Text_IO;
procedure nested is 
begin
   for x in 1..12 loop
      for y in 1..12 loop
         put(x*y, width=>4);
      end loop;
      new_line;
   end loop;
end nested;

Fortran

program nested
   integer :: x, y
   do x = 1, 12
      do y = 1, 12
         write(*,10,advance='no') xy
         10 format(I4)
      end do
      write(*,*)
   end do
end program nested

C

#include <stdio.h>
int main(void){
   int x, y;
   for (x=1; x<=12; x=x+1)
   {
      for (y=1; y<=12; y=y+1)
         printf("%4d", x*y);
      printf("\n");
   }
   return 0;
}

Julia

for x = 1:12
   for y = 1:12
      @printf("%4d", x*y)
   end
   println()
end
println(h)

spqr

Coding Ada: strings (iii) – arrays of strings

By: spqr
13 January 2021 at 20:44

The problem with strings in Ada is that they just don’t play nicely. As we have seen in previous posts, strings in Ada are either fixed, or unbounded. Now creating an array of strings in other languages isn’t that hard, nor is it in Ada – some things are just beyond its scope. Now by an array of strings, I mean a structure that could hold something like a dictionary of words. So how do we go about this?

The first example below creates a type, lexicon, with is a 5 element array of strings, 3 characters in length (so 5 three letter words). The variable words is then an instantiation of lexicon, which is assigned five three letter words. It’s not really possible to use the term string without the constraints (1..3). This is because of the fact that strings in Ada are fixed length – and it means all the strings assigned to words will have to be of length 3.

type lexicon is array(1..5) of string(1..3);
words : lexicon := ("gum","sin","cry","lug","fly");

It is also not possible to make the constraint larger, e.g. (1..10), and use words or variable length, e.g. “gum”, “housefly”, “star”. This will cause a constraint error (the word star would have to be specified as “star “, with 6 spaces filling out the word). The alternative is to unbounded strings, but they come with their own baggage. Consider the following code, changing the string to an unbounded_string.

type lexicon is array(1..5) of unbounded_string;
words : lexicon := ("gum","sin","cry","lug","star");

Compile this, and it will produce a series of errors of the form:

file.adb:10:24: expected private type "Ada.Strings.Unbounded.Unbounded_String"
file.adb:10:24: found a string type

This means that when it tried to assign the words, it considered “gum” to be a string, NOT an unbounded_string. The declaration of lexicon works fine if reading directly from a file, but not predefined strings. The fix? Not pretty, because the strings have to be converted to unbound strings using the function to_unbounded_string().

words : lexicon :=(to_unbounded_string("gum"),to_unbounded_string("sin"),to_unbounded_string("cry"),to_unbounded_string("lug"),to_unbounded_string("star"));

Messy right? There is one way of cleaning up this code and that is providing an alias for the function name to_unbounded_string(). Here is how that is done, renaming the function to tub().

function tub(Source : String) return unbounded_string renames ada.strings.unbounded.to_unbounded_string;

Then the code below it becomes:

type lexicon is array(1..5) of unbounded_string;
words : lexicon := (tub("gum"),tub("sin"),tub("cry"),tub("lug"),tub("star"));

Not perfect, but then that is Ada and strings for you.

spqr

Coding Ada: strings (iv) – unbounded to string

By: spqr
16 February 2021 at 20:52

As mentioned before, strings in Ada can be tricky. Normal strings are fixed in length, and Ada is very stringent about this. Consider a piece of code like this:

with ada.Text_IO; use Ada.Text_IO;
with ada.strings.unbounded; use ada.strings.unbounded;

procedure ustr2strfail is
   function tub(Source : String) return unbounded_string renames ada.strings.unbounded.to_unbounded_string;
   type lexicon is array(1..10) of unbounded_string;
   words : lexicon := (tub("gum"),tub("sin"),tub("cry"),
           tub("lug"),tub("star"),tub("fault"),tub("sleeper"),
           tub("a"),tub("vader"),tub("nordic"));
   aword : string(1..10);
begin
   aword := to_string(words(7));
   put(aword);
end ustr2strfail;

This will compile fine, however when it runs, the following error will propogate:

raised CONSTRAINT_ERROR : ustr2strfail.adb:12 length check failed

This means that although the unbounded string is converted to a string using the function to_string(), it only works if the length of the unbounded string equals the length of the string aword. So in the code above, aword has a length of 10, and when an attempt is made to assign it the unbounded string, “sleeper”, it fails. It would work if sleeper was padded out with 3 spaces after it. How to fix this? Do exactly that, pad the words. Here is a subroutine that does this:

with ada.Text_IO; use Ada.Text_IO;
with ada.strings.unbounded; use ada.strings.unbounded;
with ada.strings.unbounded.Text_IO; use ada.strings.unbounded.Text_IO;
with ada.strings.fixed; use ada.strings.fixed;

procedure ustr2str is
   subtype string10 is string(1..10);
   R : string10;
   function tub(Source : String) return unbounded_string renames ada.strings.unbounded.to_unbounded_string;
   type lexicon is array(1..10) of unbounded_string;
   words : lexicon := (tub("gum"),tub("sin"),tub("cry"),
           tub("lug"),tub("star"),tub("fault"),tub("sleeper"),
           tub("a"),tub("vader"),tub("nordic"));

   function padString(V: unbounded_string) return string10 is
      temp : constant string := to_string(V);
      res : string10;
   begin
      ada.strings.fixed.move(temp, res, drop=>ada.strings.right);
      return res;
   end padString;

begin
   R := padString(words(7));
   put(R);
end ustr2str;

It uses the procedure move(), from the ada.strings.fixed package. It has the following basic form:

procedure Move (Source  : in  String;
                Target  : out String;
                Drop    : in  Truncation := Error;
                Justify : in  Alignment  := Left;
                Pad     : in  Character  := Space);

The procedure move() copies characters from Source to Target. If Source has the same length as Target, then the effect is to assign Source to Target. If Source is shorter than Target then, Justify is used to place the Source into the Target (default = Left). Pad specifies the padding, default is Space. Drop is used if Source is longer than Target.

spqr

C++ versus Ada for safety critical software (i)

By: spqr
1 April 2021 at 17:33

Safety critical software is incredibly important for systems designed for applications such as aerospace, rail transportation, power stations and the like. This the F-35 hey, built largely in C++. In some cases these systems have been built in languages such as C and C++, largely because they have a very inexpensive labour force. However the use of these languages, as opposed to Ada have significant hidden costs and incur safety risks due in part to the unweildy and awkward nature of the languages. This post compares some of the features of C++ and Ada.

The problem with arrays

There are a number of issues with arrays in C++:

  • Every array index must begin at zero. Due to this array indexing is always off-by-one from the element number of the array resulting in a tendency to index past the end of the array.
  • C++ does not do bounds checking automatically, instead relying on it to be manually coded.
  • The use of 0-based arrays means that 2D arrays are indexed using a single index which is derived using a formula. This maps very closely to how the array is stored in memory, but does not support the idea of matrix in an unambiguous manner.
  • Arrays are indexed using integers.

In comparison, Ada arrays include a declaration of both the index-type, and the element type. All operations performed on the array are subjected to bounds checking, and the array indices may be of any discrete type. Below is an example of creating an array in Ada to hold a 1000×1000 image.

procedure safearrays is

subtype imgIndex is integer range 1..1000;
subtype uint8 is integer range 0..255;
type image is array(imgIndex,imgIndex) of uint8;
image1 : image;

begin
   for x in image1'range(1) loop
      for y in image1'range(2) loop
         image1(x,y) := 0;
      end loop;
   end loop;

end safearrays;

Subtypes can be used to constrain the valid range on index and array values. In this case the subtype uint8 is created to hold unsigned integers with values 0..255, and the subtype imgIndex is used to hold the index range for the image. The type image is then created using these types. If any attempt is made to access an element outside the array image, the following error occurs (where X is the line number):

raised CONSTRAINT_ERROR : safearrays.adb:X index check failed

The Ada syntax has a number of benefits. Firstly Ada arrays are first class types, not structures created as an afterthought using pointers. No pointers are involved, to at least they are handled internally. Ada also implicitly checks array bounds, meaning unlike C/C++ there will never be an issue with accessing a piece of memory not associated with an array. The Ada Range attribute means that bounds do no have to be hard coded, which makes it easier to maintain a piece of code.

The problem with strings

Let’s face it strings in C/C++ are generally horrible. Nobody should have to deal with a string terminator, the null character, ‘\0’. This means strings always have to be one element larger than the characters being stored. This means if a string is created, the null character must be added, otherwise there is the risk of issues with array bounds, or garbage being extracted from elements with no value. C/C++ provide a whole string library to manipulate strings, for example strcpy() to copy a string. Like numeric arrays, no bounds checking is performed on strings.

There are of course inherent safety concerns with C++ strings. The first is that they do not automatically maintain information about the length of the information contained in a string, and need the strlen() function to do that, assuming the string is null terminated. The second is that they do not prevent information being stored beyond the physical end of the string. A missing null character might mean the standard string functions process beyond the bounds of the string. C++ does provide a String class. Below is an example which creates a string, str, of length 10 (although in reality it should only hold 9 characters). Values are then assigned to all 10 elements of str, and 20 elements of str are printed out, as well as the length of the string.

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

int main(){
   char str[10];
   for (int i=0; i<10; i=i+1)
      str[i] = 65+i;
   for (int i=0; i<20; i=i+1)
      cout << str[i];
   cout << strlen(str) << endl;
   return 0;
}

The output is as expected from C++ – the first 10 uppercase characters followed by a bunch of garbage, and a length of 11.

    ABCDEFGHIJ
    
    `�11

Ada provides a predefined array fixed type, String. This can either be specified using bounds (line 1), or an unconstrained array (line 2), where the bounds are deduced from the initialization expression.

1   quoteyoda : String(1..30) := "Do or do not, there is no try!";
2   quoteyoda : constant String := "Do or do not, there is no try!";

Ada provides three forms of strings: the type String is a fixed length string, the type Bounded_String provides a fixed buffer than contains string data up to the length of the buffer, and Unbounded_String which uses a dynamically allocated buffer to contain data without having to define the length. There is no need for special functions to copy from one String to another, or one Bounded_String to another, however functions are provided to convert between types, for example a String to an Unbounded_String. The main benefit of strings in Ada is that memory outside the buffer associated with the string cannot be corrupted. Here is a simple example in Ada which tries to access the 11th element of the array str.

with Ada.Text_IO; use Ada.Text_IO;

procedure safestr is
str : String(1..10);
begin
   str := "ABCDEFGHIJ";
   put(str);
   put(str(11));
end safestr;

It is not possible. Compiling it produces the following messages:

safestr.adb:8:12: warning: value not in range of subtype of "Standard.Integer" defined at line 4
safestr.adb:8:12: warning: "Constraint_Error" will be raised at run time

And executing the code causes “raised CONSTRAINT_ERROR : safestr.adb:8 range check failed“. So Ada catches the error, and C++ using the standard array of characters fails to.

spqr

C++ versus Ada for safety critical software (ii)

By: spqr
2 April 2021 at 16:35

The problem with switch

The switch statement in C/C++ is horrible. It was never really that well thought out. The main problem is that the default label is entirely optional. There is no requirement to handle all possible values of an expression. The other issue is the break statement, which if not included may result in an unintended fall-through. In comparison, Ada provides a case statement which is safer. Firstly, a case statement in Ada must cover every possible value of the case expression, it will be checked at compile time. For example here is an example which classifies temperatures for an oven.

with ada.Text_IO; use Ada.Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure safecase is

   n : integer;

begin
   put("Enter an oven temperature (F): ");
   get(n);
   case n is
      when 200 .. 299 => put_line("cool oven");
      when 300 .. 325 => put_line("slow oven");
      when 326 .. 350 => put_line("moderately slow oven");
      when 351 .. 375 => put_line("moderate oven");
      when 376 .. 400 => put_line("moderately hot oven");
      when 401 .. 450 => put_line("hot oven");
      when 451 .. 500 => put_line("very hot oven");
      when others => put_line("temperature out of bounds");
   end case;

end safecase;

If the others line were omitted, then the following compiler error would propagate, implying values 199 and below, and 501 and above are not accounted for.

safecase.adb:11:04: missing case values: -16#8000_0000# .. 199
safecase.adb:11:04: missing case values: 501 .. 16#7FFF_FFFF#

A when branch can specify a single value, or a range of values, or any combination separated by the | symbol. The optional others branch covers the values not included in earlier branches. When execution of the statements in the selected branch has been completed, control resumes after the end case statement. Unlike C++, Ada does not allow fall-through to the next branch.

The problem with statements

In C/C++ there are simple statements , and compound statements enclosed in {}. It is therefore easy to create a logic error simply by failing to enclose more than one statement in a compound. Below is an example of a piece of code in which the logic is erroneous:

for (i=0, i<50; i=i+2)
   a[i] = 0;
   a[i+1] = 1;

The code is not contained within a compound statement, so only the first statement is associated with the for loop. Ada has no concept of a simple statement, and all control structures are “blocked”. For example the code above in Ada would be:

for i in 1..50 loop
   a(i*2-1) := 0;
   a(i*2) := 1;
end loop;

The problem with function returns

C++ only provides a single type of subprogram – the function. It is possible for a function to omit the name of the return type, in which case the return type is set to int. A function that returns nothing (i.e. a procedure) has a void return type. Theoretically every function should have a return type as it should be used to indicate whether a function succeeds or fails (hence the default to int). Many programmers of course don’t bother with indicating the success of a function, and hence ignore the return value. Failure to heed a function failure can result in the failure of a safety-critical system. Consider the following C++ program:

#include <iostream>

int doubled(int x){
   return x*2;
}

int quadruple(int x){
   return doubled(doubled(x));
}

int main(){
   quadruple(5);
   return 0;
}

When this is compiled (even with warnings) there is no issue, even though the function call to quadruple() is treated like like a statement, with the return sent off to the nether world. It is then easy to have such failings happen in a large program, and hard to trace a source of error. Ada return values can be used for the same purposes as C/C++ return values, but they cannot be ignored. Here is the same program coded in Ada.

with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure safefunc is
   n : Integer;

   function quadruple (x: Integer) return Integer is
      function double (x: Integer) return Integer is
      begin
         return x * 2;
      end double;
      result : Integer;
   begin
      result := double(double(x));
      return result;
   end Quadruple;

begin
   quadruple(5);
end safefunc;

Compiling this will lead to an error on line 18, the source of the call to quadruple():

safefunc.adb:18:04: cannot use call to function "quadruple" as a statement
safefunc.adb:18:04: return value of a function call cannot be ignored

This has to be fixed by adding a return value, of the form:

n := quadruple(5);

Ada was designed to support safety in modern safety critical systems. The examples outlined offer an insight into how Ada is better than C++ for such systems. Neither C nor C++ were designed to support the needs of real-time safety-critical systems.

spqr

Coding Ada: Timing code and passing functions to functions

By: spqr
19 March 2019 at 14:03

If you have to time a function in a language like Ada, one option is to create a timer function that takes the function to be timed as a parameter. Here is an Ada program that times the Ackermann function. There is nothing special about the ackermann() function, except that it is recursive. The interesting part of the code involves creating a type, functype, to hold the function “pointer”, or access point. The type uses the phrase “access function” followed by the parameter list for the function in question, and the type of the return value. This denotes an Access type, which is the equivalent of a pointer in other languages. Rather than using the term “points to”, Ada prefers to refer to this type of entity as “granting access” to an object. An access to subprogram allows the caller to call a subprogram without knowing its name nor its declaration location.

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

procedure passingfunc is

   type functype is access function (x,y: integer) return integer;

   function ackermann(m,n: in integer) return integer is
   begin
      if m = 0 then
         return n+1;
      elsif n = 0 then
         return ackermann(m-1,1);
      else
         return ackermann(m-1,ackermann(m,n-1));
      end if;
   end ackermann;

   procedure timeit(Func : functype; m,n : in integer) is 
      result : integer;
      startTime, endTime : time;
      milliS : Duration;
   begin
      startTime := Clock;
      result := Func(m,n);
      endTime := Clock;
      milliS := (endTime - startTime) * 1000;
      put_line("Result: " & Integer'Image(result));
      put_line("Runtime = " & Duration'Image(milliS) & " milliseconds.");
   end timeit;

begin
   timeit(ackermann'Access, 4, 1);
end passingfunc;

The function timeit() can then be implemented. It declares Func as one of the parameters, implying that a function (pointer) can be passed to it. The remaining two parameters are the two parameters to be passed onto ackermann(). Note that the call to Func() is quite normal as well – no magic needed. The guts of the timing algorithm are described in another post, so the interested reader is referred there. When the function timeit() is called in the main program, the ackermann() function is passed using ackermann’Access.

Here’s the program running… with some trivial output:

Result: 65533
Runtime = 17441.122000000 milliseconds.

Those of you who have ever implemented a non-recursive version of Ackermann will note the runtime of 17-odd seconds, which is much faster than the 60-80 seconds of the version using a stack.

spqr

Coding Ada: Bitwise operators

By: spqr
7 April 2020 at 02:14

In Ada, you can find similar bitwise operators to other languages: and, or, xor, and operators that shift left and right. The trick is that they use modular types. These types are unsigned and have “wrap-around” semantics. Consider the following declaration:

subtype byte is unsigned_8;
package byteIO is new ada.text_io.modular_io(byte);
x, y, z: byte;

This defines an integer type whose values range from 0 to 255 (on most processors), i.e. the integers that will fit in a byte. Since it’s “modular”, it means that arithmetic operations on the type wrap around (i.e. if you add 130 + 130, you will get 4. With this declaration, you can create binary numbers:

x := 2#00011110#;
y := 2#11110100#;
z := 2#11110000#;

Here x=30, y=244, and z=240. So we can then use bitwise operators (found in the Ada package Interfaces) to swap the numbers:

x := x xor y;
y := y xor x;
x := x xor y;

The numbers can be printed out with this code:

put_line(unsigned_8'image(x));
put_line(unsigned_8'image(y));

You can also use put from the sub-package byteIO:

byteIO.put(item => z, base => 2);

If we want to convert a number input from integer to type byte, we can do so in the following manner:

w: integer;
get(w);
z := byte'val(integer'pos(w));
byteIO.put(item => z, base => 2);

The byte’val returns the base type of byte. For example, if the value input by the user is 17 (w), then the value assigned to z will be 10001. What about shift operators? There is shift_left() and shift_right(). Here’s an example:

zs: byte; 
zs := shift_left(z,1);
byteIO.put(item => zs, base => 2);

If the user inputs 12, then the value of zs will be 24, as it has been shifted left 1 bit.

spqr

Programming made easy – loops (i)

By: spqr
23 April 2020 at 19:53

Humans are, by nature, repetitive beings. Watch the Star Trek: TNG episode “Cause and Effect”, and you will quickly understand what a loop is as the Enterprise is caught in a causality time loop. There are also times when programs must do a task more than once. If the repetition is limited to some countable number of recurrences with a foreseeable end then it is called a loop. And if, at least in the perception of the individual who is experiencing a certain situation, there is no end in sight then one is talking about endless or infinite loops. All of the above can be desirable or not. Here is a visual illustration:

The code in the program moves linearly from statement A, until it encounters a loop. At the start of the loop, the loop control tests some condition. If the condition is true, then the loop activates the piece of code (made up of one or more statements) in what is called the loop body. When that code is done the loop returns to the loop control, and the process starts all over. If the condition in the loop control becomes false, nothing happens, and the program ends the loop, and continues on to statement B. There is also a possibility that the first time into the loop, the condition becomes false, and in that case the loop is bypassed.

Loops are structures that allow for repetition – repeating an activity a number of times. There are different kinds of repetition:

  1. Loops where the number of repetitions is known before the loop activates.
  2. Loops where the number of repetitions isn’t known, and is controlled by some condition.
  3. Loops that are infinite, i.e. could continue to infinity and beyond.

The best example of the first case encountered in most languages is the for loop. Here we use a variable called a loop index to control the loop. Each time a loop loops it is called an iteration. So if the loop index is x, and it repeats 100 times, then x will begin with a value of 1, and increment by 1 each iteration, until x reaches 100. In Julia this is easily achieved in the following manner (in this case the loop simply prints out each value of x):

for x = 1:100
   println(x)
end

In Ada it takes a similar format (the printing part is a little different, but just ignore that bit):

for x in 1..100 loop
    put(x); 
    new_line;
end loop;

Ada uses the keyword loop to signify it as a loop. Fortran also uses a similar construct, but has never used the for loop, opting instead for the keyword do. Same concept, different name.

do x = 1, 100
   write(*,*) x
end do

C also uses a for loop, although it can be somewhat more convoluted:

for (x=1; x<=100; x=x+1)
   printf("%d\n",x);

All have their varied syntax, but achieve the same goal – a known number of iterations.

spqr

Programming made easy – loops (ii)

By: spqr
24 April 2020 at 14:39

With the basics of for loops, let’s look at an example of an actual program which calculates the Harmonic series, which is an infinite series of the form:

h(n) = 1 + 1/2 + 1/3 + … + 1/n

The code for the program in Fortran, Ada, Julia and C is shown below. The loop is highlighted in blue.  For interest sake, the loops are presented in reverse, i.e. 1/n + 1/(n-1) + … + 1, as it illustrates clearly how each language deals with the simple issue of a decreasing index. In each case the starting value of the index variable i is n, and the ending value is 1. Here it is the algorithm for the Harmonic series depicted visually, clearly showing the role of the loop:

Fortran

program harmonic
   integer :: n, i
   real :: h

   read (*,*) n
   h = 0
   do i = n,1,-1
      h = h + 1.0/i
   end do
   write(*,*) h
end program harmonic

Here the third item (the modifier) in the Fortran loop denotes the type of change to the loop index, in this case, a decrease by 1. In a  normal loop incrementing by 1, the modifier can be omitted.

C

#include <stdio.h>

int main(void){
   int i, n;
   float h;

   scanf("%d", &n);
   printf("%d\n", n);
   h = 0.0;
   for (i=n; i>=1; i=i-1)
      h = h + 1.0/i;
   printf("%lf\n", h);
   return 0;
}

In the C version, the loop index is decremented using the statement i=i–1. For more than one statement after the for loop definition, the statements would have to be encapsulated in { and }.

Ada

with ada.Float_Text_IO; use Ada.Float_Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure harmonic is
   n : integer;
   h : float;
begin
   get(n);
   h := 0.0;
   for i in reverse 1..n loop
      h := h + 1.0/float(i);
   end loop;
   put(h);
end harmonic;

In Ada, the keyword reverse is used to specify a loop will be decreasing in value.

Julia

n = parse(Int64, chomp(readline()))
println(n)
h = 0
for i = n:-1:1
   h = h + 1.0/i
end
println(h)

Here the index modifier is placed in the centre, n:-1:1, implying i has the values n to 1, modified by -1 each loop iteration. In a  normal loop incrementing by 1, the modifier can be omitted.

spqr

Programming made easy – loops (iii)

By: spqr
27 April 2020 at 13:44

There are of course other loops. These loops are normally used when the number of times a loop iterates isn’t known. These type of loops generally use some form of condition to control the loop. The most prominent type of loop is the while loop. A while loop checks a condition, and if true, processes the code in the loop body, otherwise it exists the loop. Unlike the for loop, which controls the values of the loop index in terms of updating its value, in the loops like while, this process must be added, i.e. the value of i is given the value 1, before the loop is activated, and its value is incremented within the loop body.

We will consider some of these loops using the same Harmonic series, this time in increasing order. The code for the program in Fortran, Ada, Julia and C is shown below. The loop is highlighted in blue.

Fortran

program harmonic
   integer :: n, i
   real :: h

   read (*,*) n
   h = 0
   i = 1
   do while (i <= n)
      h = h + 1.0/i
      i = i + 1
   end do
   write(*,*) h
end program harmonic

Here the index variable, i, must be initiated to the value 1 before the loop begins, and incremented within the loop. The condition states that the loop continues while the value of i is less than or equal to n, i.e. (i <= n) . Fortran also provides a more generic do/end do loop.

C

#include <stdio.h>

int main(void){
   int i, n;
   float h;

   scanf("%d", &n);
   printf("%d\n", n);
   h = 0.0;
   i = 1;
   while (i <= n){
      h = h + 1.0/i;
      i = i + 1;
   }
   printf("%lf\n", h);
   return 0;
}

In the C version, the structure is almost the same, except that the do while of Fortran has been replace with a simple while. With the addition of the statement to increment the value of i, the loop “contents” are now enclosed within { and }. C also has another loop, the do/while() loop, that performs the loop once, then evaluates the condition at the end.

Ada

with ada.Float_Text_IO; use Ada.Float_Text_IO;
with ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure harmonic is
   n, i : integer;
   h : float;
begin
   get(n);
   h := 0.0;
   i := 1;
   while i <= n loop
      h := h + 1.0/float(i);
      i := i + 1;
   end loop;
   put(h);
end harmonic;

In Ada, the while loop looks similar to that of the other to Fortran and C, it is just subtleties in syntax that set them apart. Here there are no parenthesis around the condition. Also note that in the for loop, the index variable, i, did not have to be declared, but here it does. Ada also provides a generic loop/end loop loop.

Julia

n = parse(Int64, chomp(readline()))
println(n)
h = 0
i = 1
while i <= n
   h = h + 1.0/i
   i = i + 1
end
println(h)

Same deal here. Similar to Ada, except the loop keyword after the condition isn’t needed. There are only two loops in Julia.

As you can see, over four languages, the only thing that fundamentally changes is the basic syntax of the loop.

spqr

Programming made easy – nested loops

By: spqr
1 May 2020 at 13:21

We’re not quite done with loops yet. There are still a few things to learn, although some can be left for later. There are certain situations where it is appropriate to use a loop inside another loop. Such a loop is called a nested loop. A nested loop is executed, essentially, from the inside out. Each loop is like a layer and has its own loop control, its own condition and its own loop body. Below is a schematic of three nested loops.

A loop inside a loop, inside a loop.

There are many different kinds of nested loops, and they are largely dependent on the underlying algorithm. Below are some examples of the types of nested loops that are possible.

Various examples of nested loops

We will consider how a nested loop can be used to create a times table. It basically works by iterating the inner loop 12 times for every iteration of the outer loop. So, 12 × 12 = 144 iterations. The outer loop is highlighted in blue, and the inner loop in red. Here is a sample output:

 1   2   3   4   5   6   7   8   9  10  11  12
 2   4   6   8  10  12  14  16  18  20  22  24
 3   6   9  12  15  18  21  24  27  30  33  36
 4   8  12  16  20  24  28  32  36  40  44  48
 5  10  15  20  25  30  35  40  45  50  55  60
 6  12  18  24  30  36  42  48  54  60  66  72
 7  14  21  28  35  42  49  56  63  70  77  84
 8  16  24  32  40  48  56  64  72  80  88  96
 9  18  27  36  45  54  63  72  81  90  99 108
10  20  30  40  50  60  70  80  90 100 110 120
11  22  33  44  55  66  77  88  99 110 121 132
12  24  36  48  60  72  84  96 108 120 132 144

Ada

with ada.Text_IO; use Ada.Text_IO;
with ada.Integer_Text_IO; use ada.Integer_Text_IO;
procedure nested is 
begin
   for x in 1..12 loop
      for y in 1..12 loop
         put(x*y, width=>4);
      end loop;
      new_line;
   end loop;
end nested;

Fortran

program nested
   integer :: x, y
   do x = 1, 12
      do y = 1, 12
         write(*,10,advance='no') xy
         10 format(I4)
      end do
      write(*,*)
   end do
end program nested

C

#include <stdio.h>
int main(void){
   int x, y;
   for (x=1; x<=12; x=x+1)
   {
      for (y=1; y<=12; y=y+1)
         printf("%4d", x*y);
      printf("\n");
   }
   return 0;
}

Julia

for x = 1:12
   for y = 1:12
      @printf("%4d", x*y)
   end
   println()
end
println(h)

spqr

❌
❌