C++ versus Ada for safety critical software (ii)
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