is procedure - AdaCore Libre

Programming in the Small:
C/C++/Java Pitfalls
& Ada Benefits
Franco Gasperoni
[email protected]
http://libre.adacore.com/Software_Matters
Suggested Reading
C Traps and Pitfalls
• by Andrew Koenig (Addison Wesley)
Guidelines for the Use of the C Language in Vehicle Based
Software
• Purchasing info at http://www.misra.org.uk/misra-c.htm
Multilanguage Programming on the JVM: The Ada 95
Benefits
• http://libre.act-europe.fr/Why_Ada/ada-on-jvm.pdf
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
3
Lecture Summary
In this lecture we concentrate on programming in the small
We go over some Ada basic constructs
• Procedures, functions, types, loops, if and case statements exceptions,
etc.
We show some C/C++/Java pitfalls
We show how Ada avoids them
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
5
Programming in the Small with C
C makes the following assumption:
• Trust the programmers, they never make mistakes
• Favor program conciseness over its readability
But:
• Programmers do make mistakes
• Programs are written once but read many times
The C foundation of C++ & Java leads to fragile software
• Software where it is easy to make mistakes
• Software that is hard to read
• Software that is hard to change
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
12
Note on the Ada Development Environment
In this course we use GNAT GAP Edition
GNAT is widely available
• You can download the sources of GNAT
• Pre-built GNAT binaries available for: Linux, Solaris, Windows
GNAT GPL Edition available at
• http://libre.adacore.com
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
14
Background on Ada Programming
A Simple Example
Procedure Main
Procedure Main
•
•
with Text_IO;
procedure Main is
Length : Integer := 8265;
Width : Integer := 0252;
Height : Integer := 8292;
begin
Text_IO . Put_Line (Length'img);
Text_IO . Put_Line (Width'img);
Text_IO . Put_Line (Height'img);
end Main;
Note: Ada is case insensitive
http://libre.adacore.com
Stored in file main.adb
Technically this is called a compilation unit
A compilation unit can be the body or
the spec (specification) of a:
•
•
•
procedure
function
package (see next lecture)
Spec = precise list of services exported
Body = implementation details
In GNAT:
•
•
•
1 compilation unit per file
File name matches unit name
2 file extensions possible
- .adb = Ada Body
- .ads = Ada Spec
© AdaCore under the GNU Free Documentation License
16
Inside Procedure Main
with Text_IO;
procedure Main is
Length : Integer := 8265;
Width : Integer := 0252;
Height : Integer := 8292;
begin
Text_IO . Put_Line (Length'img);
Text_IO . Put_Line (Width'img);
Text_IO . Put_Line (Height'img);
end Main;
http://libre.adacore.com
Declarative part: Contains the declaration of:
•
Variable, types, nested procedures, nested
functions, ... used in procedure Main
Procedure statements
© AdaCore under the GNU Free Documentation License
17
with Text_IO;
List of compilation units whose
services are used in procedure Main
with Text_IO;
procedure Main is
Length : Integer := 8265;
Width : Integer := 0252;
Height : Integer := 8292;
begin
Text_IO . Put_Line (Length'img);
Text_IO . Put_Line (Width'img);
Text_IO . Put_Line (Height'img);
end Main;
http://libre.adacore.com
Text_IO is the predefined Ada text
Input/Output library
Procedures declared in library Text_IO
and used in Main
© AdaCore under the GNU Free Documentation License
18
use Text_IO;
with Text_IO; use Text_IO;
procedure Main is
Length : Integer := 8265;
Width : Integer := 0252;
Height : Integer := 8292;
begin
Text_IO . Put_Line (Length'img);
Text_IO . Put_Line (Width'img);
Text_IO . Put_Line (Height'img);
end Main;
http://libre.adacore.com
By putting a use clause you can (but don't
have to) omit the Text_IO prefix inside Main
© AdaCore under the GNU Free Documentation License
19
The ' img Attribute
with Text_IO; use Text_IO;
procedure Main is
Length : Integer := 8265;
Width : Integer := 0252;
Height : Integer := 8292;
begin
Text_IO . Put_Line (Length ' img);
Text_IO . Put_Line (Width ' img);
Text_IO . Put_Line (Height ' img);
end Main;
http://libre.adacore.com
' img is a predefined GNAT attribute
Given an integer or floating point number X,
X ' img returns its string representation
• More on attributes later on
•
© AdaCore under the GNU Free Documentation License
20
Structure of an Ada Program
General Structure of an Ada Program
with …;
with …;
with …;
with …;
with …;
with …;
procedure Some_Main is
…
begin
…
end Some_Main;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
22
A Simple Example
with Text_IO; use Text_IO;
procedure Display (S : String; X, Y : Integer) is
begin
Put_Line (S & " (" & X'img & ") = " & Y'img);
end Display;
function Fact (N : Integer) return Integer is
begin
if N <= 1 then
return 1;
else
return N * Fact (N - 1);
end if;
end Fact;
with Display;
with Fact;
procedure Display_Fact is
begin
for K in 1 .. 4 loop
Display ("Factorial", K, Fact (K));
end loop;
end Display_Fact;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
23
Building & Executing Display_Fact
GNAT has an automatic "make"
facility: gnatmake
Gnatmake
Will compile or recompile the Ada
sources that need to be compiled
• It will bind and link them
•
A make file is not necessary
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
24
Note on the Concatenation Operator &
with Text_IO; use Text_IO;
procedure Display (S : String; X, Y : Integer) is
begin
Put_Line (S & " (" & X'img & ") = " & Y'img);
end Display;
1
2
3
4
5
&
A
B
=
1
2
3
4
5
A
B
1 dimensional arrays
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
25
Note on For Loops
with Display;
with Fact;
procedure Display_Fact is
begin
for K in 1 .. 4 loop
Display ("Factorial", K, Fact (K));
end loop;
end Display_Fact;
http://libre.adacore.com
In for loops, the loop variable (K in
the example) is implicitly declared
The loop variable cannot be
modified inside the for loop
The loop variable ceases to exist
just after the loop
© AdaCore under the GNU Free Documentation License
26
Numeric Pitfall
in C/C++/Java
What is the Program Output ?
#include <stdio.h>
This program compiles fine
int main () {
int length = 8265;
int width = 0252;
int height = 8292;
What is its output?
printf ("length = %d\n", length);
printf ("width = %d\n", width);
printf ("height = %d\n", height);
}
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
28
Surprised ?
Was this the programmer’s intent ?
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
29
Numbers in C/C++/Java
In C/C++/Java numbers starting with 0 are octal numbers
This is a bad choice
• Error-prone
• Hard-to-read
There is no way to specify numbers in base 2
• Very surprising giving the fact that C was meant for to be a low-level
systems language
Never use octal numbers
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
30
The Ada Version
with Text_IO;
procedure Main is
Length : Integer := 8265;
Width : Integer := 0252;
Height : Integer := 8292;
begin
Text_IO . Put_Line (Length ' img);
Text_IO . Put_Line (Width ' img);
Text_IO . Put_Line (Height ' img);
end Main;
No surprises in
Ada
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
31
Numbers in Ada
Length
Width
Width_8
: Integer := 8265;
: Integer := 0252;
-- regular decimal number
: Integer := 8#252#; -- octal number
B_Mask
: Integer := 2#1100_1011#;
-- binary number
-- you can use “_” to separate digits
W_Mask : Integer := 16#FFF1_A4B0#; -- Hexadecimal number
If no base is specified the number a decimal number
In Ada you can specify any base from 2 to 16 (for both integer and real
(floating point) numbers
Use the “_” to separate digits for clarity
•
http://libre.adacore.com
1_000_000_000
© AdaCore under the GNU Free Documentation License
32
Lexical & Syntactic Pitfalls
in C/C++/Java
Is the Following Code Correct ?
#include <limits.h>
/* If *y is zero and x > 0 set *k to the biggest positive integer.
* If *y is zero and x <=0 leave *k unchanged.
* If *y is not zero set *k to be x divided by *y and increment *y by 1.
*/
void check_divide (int *k, int x, int *y) {
if (*y = 0)
if (x > 0)
*k = INT_MAX;
else
*k = x / *y /* it is safe to divide by *y since it cannot be 0 */;
*y++;
}
This program compiles fine, but has a number of problems. Which ones?
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
34
There are 4 Bugs
= versus ==
• Using “=“ for assignment and “==“ for equality is a poor choice
• Use a compiler that warns you when you use “=“ inside tests
• This is a hard problem because C++ style encourages the use of “=“
inside tests:
while (*s1++ = *s2++);
Dangling else problem
• Always bracket everything
Nested y++
Bad operator precedence *y++ means *(y++)
• When in doubt use parentheses or separate tokens with white spaces ...
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
35
What about Java?
boolean safety_flag;
boolean danger_flag;
• This problem exists in Java for boolean,
…
but has been fixed for other data types if (safety_flag = danger_flag) {
sound_alarm ();
Dangling else problem
}
• Problem is still there
This is OK in Java
Bad operator precedence *j++
but is often a bug
= versus ==
means *(j++)
• No * operator in Java. This Problem has
been solved
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
36
The Correct Version
#include <limits.h>
/* If *y is null and x > 0 set *k to the biggest positive integer.
* If *y is null and x <=0 leave *k unchanged.
* If *y is non null set *k to be x divided by *y and increment *y by 1.
*/
void check_divide (int *k, int x, int *y) {
if (*y == 0) {
if ( x > 0)
*k = INT_MAX;
}
else {
*k = x / *y /* it is safe to divide by *y since it cannot be 0 */;
(*y)++;
/* or *y ++ */
}
}
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
37
Ada Solves all the Previous Pitfalls
-- If Y = 0 and X > 0 set K to the biggest positive integer.
-- If Y = 0 and X <= 0 leave K unchanged.
-- If Y /= 0 set K to be X divided by Y and increment Y by 1.
procedure Check_Divide (K : in out Integer; X : Integer; Y : in out Integer) is
begin
if Y = 0 then
if X > 0 then
K := Integer’Last; -- K is set to the largest Integer
end if;
else
K := X / Y; -- it is safe to divide by Y since it cannot be 0
Y := Y + 1;
end if;
end Check_Divide;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
38
Simpler Ada Version: "elsif" "and then"
-- If Y = 0 and X > 0 set K to the biggest positive integer.
-- If Y = 0 and X <= 0 leave K unchanged.
-- If Y /= 0 set K to be X divided by Y and increment Y by 1.
procedure Check_Divide (K : in out Integer; X : Integer; Y : in out Integer) is
begin
if Y = 0 and then X > 0 then
K := Integer’Last; -- K is set to the largest Integer
elsif Y /= 0 then
K := X / Y; -- it is safe to divide by Y since it cannot be 0
Y := Y + 1;
end if;
end Check_Divide;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
39
Lexical & Syntactic Clarity in Ada
= means equality while := means assignment
• If you use one instead of the other you get a compiler error
No dangling else in Ada
•
if … then
… -- Must be terminated by an end if;
end if;
• If it isn't then you get a compiler error
In Ada comments start with -- and go to the end of the line.
No other type of comment
No ++ operators in Ada
No need for a * operator in Ada
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
40
Side Note on Ada Attributes
Attributes
Ada types, objects, and other entities can have attributes
An attribute is a property of the type, object, etc
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
45
Example of Scalar Attributes
Given T some scalar type (Integer, Float, etc)
Given X an object of type T
T ' First
Smallest value in T
T ' Last
Largest value in T
T ' image (X)
String representation of X
In GNAT you can use X ' img
instead of T ' image (X)
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
46
Example of Integer ' Last
procedure Check_Divide_And_Increment (K : in out Integer; X : Integer; Y : in out Integer) is
begin
if Y = 0 then
if X > 0 then
K := Integer’Last; -- K is set to the largest Integer
end if;
else
K = X / Y; -- it is safe to divide by Y since it cannot be 0
Y := Y + 1;
end if;
end Checked_Divide;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
47
More C/C++/Java Syntactic Pitfalls
Is the Following Code Correct ?
// If the signal ahead is clear then increase the speed.
void increase_speed_if_safe (int speed, int signal) {
if (signal == CLEAR);
increase_speed ();
}
This program compiles fine, but has a problem. Which one?
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
49
Be Careful of Spurious Semicolons
// If the signal ahead is clear then increase the speed.
void increase_speed_if_safe (int speed, int signal) {
if (signal == CLEAR);
increase_speed ();
}
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
51
The Ada Version is Always Safe
-- If the signal ahead is clear then increase the speed.
procedure increase_speed_if_safe (speed : integer; signal : integer) is
begin
if signal = CLEAR then
increase_speed;
end if;
end increase_speed_if_safe;
If you write
if signal = CLEAR then ;
• You get a compiler error
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
52
More Bad Luck in C/C++/Java:
Enumerations and Switch Statements
enum Alert_Type {LOW, MEDIUM, HIGH, VERY_HIGH};
// C or C++. Java does not have enumerations, you have to use ints instead
void handle_alert (enum Alert_Type alert) {
switch (alert) {
case LOW:
activate_camera ();
case MEDIUM:
send_guard ();
case HIGH:
sound_alarm ();
}
}
void process_alerts () {
handle_alert (2);
…
This program compiles fine, but has a number of problems. Which ones?
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
53
Defects in the Previous Code
Don't forget break statements
C/C++/Java do not check that you
have treated all cases in the switch
case labels can be integers or
(values of) any enum type, not just
enum Alert_Type which in most
cases will be an error
http://libre.adacore.com
void handle_alert (enum Alert_Type alert) {
switch (alert) {
case LOW:
activate_camera ();
break;
case MEDIUM:
send_guard ();
break;
case HIGH:
sound_alarm ();
break;
case VERY_HIGH:
alert_police ();
break;
}
}
void process_alerts () {
handle_alert (HIGH);
© AdaCore under the GNU Free Documentation License
54
Ada is Safer (and Less Verbose)
type Alert_Type is (LOW, MEDIUM, HIGH, VERY_HIGH);
procedure Process_Alert (Alert : Alert_Type) is
begin
case Alert is
when LOW
=>
Activate_Camera;
when MEDIUM
=>
Send_Guard;
when HIGH
=>
Sound_Alarm;
when VERY_HIGH =>
Alert_Police;
end case;
end Process_Alert;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
No break statements
Ada will check that you
have treated all cases in
the case statement
You can only use an object
of type Alert_Type
55
Combining Cases
procedure Process_Alert (Alert : Alert_Type) is
begin
case Alert is
when LOW
=>
Activate_Camera;
when MEDIUM
=>
Send_Guard;
when HIGH
| VERY_HIGH =>
Sound_Alarm;
Alert_Police;
end case;
end Process_Alert;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
56
Using a Default Clause
procedure Process_Alert (Alert : Alert_Type) is
begin
case Alert is
when LOW
=>
Activate_Camera;
when MEDIUM =>
Send_Guard;
when others =>
Sound_Alarm;
Alert_Police;
end case;
end Process_Alert;
Ada "when others" is equivalent to C/C++/Java "default", and takes away
most of the benefit of the checking for all cases covered
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
57
Using a Range
procedure Process_Alert (Alert : Alert_Type) is
begin
case Alert is
when LOW
=>
Activate_Camera;
when MEDIUM .. VERY_HIGH =>
Send_Guard;
Sound_Alarm;
Alert_Police;
end case;
end Process_Alert;
A range is a set of ordered values
•
MEDIUM .. VERY_HIGH = MEDIUM, HIGH, VERY_HIGH
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
58
Enumeration Types in Ada
type Alert is (LOW, MEDIUM, HIGH, VERY_HIGH);
procedure P (B : Integer) is
A : Alert;
begin
Compilation error
A := B;
Enumerations are true types in Ada
In C enums are just integers
In C++ enums are implicitly converted to ints (not from ints)
// C++
enum Alert {LOW, MEDIUM, HIGH, VERY_HIGH};
int k = LOW;
Alert a = 1;
http://libre.adacore.com
// accepted by C++
// rejected by C++
© AdaCore under the GNU Free Documentation License
59
Ada Enumeration Types and Attributes
http://libre.adacore.com
Alert_Type ' First
LOW
Alert_Type ' Last
VERY_HIGH
© AdaCore under the GNU Free Documentation License
61
Predefined Enumerations in Ada
http://libre.adacore.com
type Boolean
is (False, True);
type Character
is (…, 'a', 'b', 'c', …);
© AdaCore under the GNU Free Documentation License
62
Predefined Enumerations: Examples
function Is_Letter (C : Character) return Boolean is
begin
return (C in 'a' .. 'z') or (C in 'A' .. 'Z');
end Is_Letter;
function Is_Arithmetic_Operator (C : Character) return Boolean is
begin
case C is
when '+' | '-' | '*' | '/' =>
return True;
when others =>
return False;
end case;
end Is_Arithmetic_Operator;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
63
C/C++/Java Type System
What is a Type?
A type is characterized by:
The set of values an expression of that type can take
The operations that can be applied to those values
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
71
Pre-Defined and User-Defined Types
Some types can be pre-defined by the language
• E.g. booleans, integers, characters, strings, etc
Pre-defined types come with pre-defined operations
• E.g. for integers: additions, subtractions, etc.
Languages typically allow user-defined types and operations
• User-defined operations are provided in the form of procedures and
functions
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
72
Typing Problems Common to C/C++/Java
typedef in C/C++ is a shorthand it does not define a new type
No user-defined types
•
•
•
Scalars (characters, integers, reals)
Pointers (e.g. there can only be a single pointer to an int type)
Arrays (e.g. there can only be a single array of int type)
Implicit conversions from integers to reals
Weak overflow semantics rules for signed integers
Missing types
•
•
•
•
•
http://libre.adacore.com
Enumerations in Java (not full types in C/C++)
Character types in C/C++
Fixed points
Unsigned integers in Java
Pointers to functions in Java
© AdaCore under the GNU Free Documentation License
78
C/C++ Example
typedef int Time;
typedef int Distance;
typedef int Speed;
…
const Speed SAFETY_SPEED = 120;
…
void increase_speed (Speed s);
…
void check_speed (Time t, Distance d) {
Speed s = d/t;
if (s < SAFETY_SPEED)
increase_speed (t);
}
void perform_safety_checks () {
Time
t = get_time ();
Distance d = get_distance ();
…
check_speed (d, t);
}
http://libre.adacore.com
The program to the left compiles fine
There is something wrong with it
What ?
© AdaCore under the GNU Free Documentation License
80
What's Wrong with C/C++
typedef int Time;
typedef int Distance;
typedef int Speed;
…
const Speed SAFETY_SPEED = 120;
…
void increase_speed (Speed s);
…
void check_speed (Time t, Distance d) {
Speed s = d/t;
if (s < SAFETY_SPEED)
increase_speed (t);
}
void perform_safety_checks () {
Time
t = get_time ();
Distance d = get_distance ();
…
check_speed (d, t);
}
http://libre.adacore.com
Program compiles fine but has 2
serious flaws that go undetected
FLAW 1:
t is a Time
increase_speed() takes a Speed
parameter
• Time and Speed are conceptually
different, they should not be mixed up
•
•
FLAW 2:
Distance and Time parameters have
been inverted
• Time and Distance are conceptually
different, they should not be mixed up
•
C/C++ provide NO HELP to the
programmer in detecting these
mistakes
© AdaCore under the GNU Free Documentation License
82
Things are Even Worse in Java
There are no typedef in Java
Everything must be an int
typedef are useful for documentation
purposes
typedef could be used to perform sanity
checks during code walkthroughs or
with simple tools
This problem is particularly severe in
Java given that many API calls have
several indistinguishable int parameters:
•
final int SAFETY_SPEED = 120;
…
void check_speed (int t, int d) {
int s = d/t;
if (s < SAFETY_SPEED)
increase_speed (t);
}
void increase_speed (int s) { … }
void perform_safety_checks () {
int t = get_time ();
int d = get_distance ();
…
check_speed (d, t);
}
AdjustmentEvent (Adjustable source, int id,
int type, int value)
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
83
What About Ada?
You can write the same buggy
code in Ada, but …
… Ada has two lines of defense
that do not exist in C/C++ or
Java to protect the programmer
• User defined types
• Parameter associations
http://libre.adacore.com
-- Buggy code. DON'T write this
SAFETY_SPEED : constant Integer := 120;
…
procedure Increase_Speed (S : Integer);
…
procedure Check_Speed (T : Integer; D : Integer) is
S : Integer := D / T;
begin
if S < SAFETY_SPEED then
Increase_Speed (T);
end if;
end Check_Speed;
procedure Perform_Safety_Checks is
T : Integer := Get_Time;
D : Integer := Get_Distance;
begin
…
Check_Speed (D, T);
end Perform_Safety_Checks;
© AdaCore under the GNU Free Documentation License
84
Defining New Types in Ada
Users can define their own types in Ada
In C/C++/Java users can only define struct/union/class types
• No user-defined scalar, pointer or array types
-- Example of integer type definition in Ada
type Time
is range 0 .. 3_600;
type Distance is range 0 .. 1_000;
type Speed
http://libre.adacore.com
is range 0 .. 4_000;
© AdaCore under the GNU Free Documentation License
85
User Defined Integer Types in Ada
Each user defined integer type introduces
a new type
type Time
is range 0 .. 3_600;
This new type is NOT a synonym of Integer
type Distance is range 0 .. 1_000;
type Speed
is range 0 .. 4_000;
Each user defined integer type gives its
bounds, i.e. the values any object of this
type can take
•
•
http://libre.adacore.com
Time ' First = 0
Time ' Last = 3_600
© AdaCore under the GNU Free Documentation License
86
Ada is Strongly Typed (1 of 2)
type Time
is range 0 .. 3_600;
type Distance is range 0 .. 1_000;
type Speed
is range 0 .. 4_000;
SAFETY_SPEED : constant Speed := 120;
procedure Increase_Speed (S : Speed);
procedure Check_Speed (T : Time; D : Distance) is
Compilation
S : Speed := D / T;
error
begin
if S < SAFETY_SPEED then
Compilation
Increase_Speed (T);
error
end if;
end Check_Speed;
…
http://libre.adacore.com
When you define the proper types
the Ada compiler catches the errors
To mix different types you must
use explicit conversions in Ada
D is of type Distance, T is of type
Time, S is of type Speed
•
Only objects of the same type can be
mixed together in this fashion
Increase_Speed is expecting a
Speed parameter not a Time
© AdaCore under the GNU Free Documentation License
87
Ada is Strongly Typed (2 of 2)
type Time
is range 0 .. 3_600;
type Distance
is range 0 .. 1_000;
type Speed
is range 0 .. 4_000;
…
procedure Check_Speed (T : Time; D : Distance);
…
procedure Perform_Safety_Checks is
T : Time
:= Get_Time;
D : Distance := Get_Distance;
begin
…
Compilation
Check_Speed (D, T);
error
end Perform_Safety_Checks;
http://libre.adacore.com
Parameters switched
© AdaCore under the GNU Free Documentation License
88
The Correct Ada Version
type Time
is range 0 .. 3_600;
type Distance is range 0 .. 1_000;
type Speed is range 0 .. 4_000;
SAFETY_SPEED : constant Speed := 120;
procedure Increase_Speed (S : Speed);
procedure Check_Speed (T : Time; D : Distance) is
S : Speed := Speed ( Integer(D) / Integer (T));
begin
if S < SAFETY_SPEED then
Increase_Speed (S);
end if;
end Check_Speed;
procedure Perform_Safety_Checks is
T : Time
:= Get_Time;
D : Distance := Get_Distance;
begin
…
Check_Speed (T, D);
end Perform_Safety_Checks;
http://libre.adacore.com
You must convert D and T to
Integer to perform the division
And then convert the result to
type Speed
© AdaCore under the GNU Free Documentation License
89
But What About?
type A_Type is …;
How do you know it was
procedure Safe_Copy (Source : A_Type; Target : A_Type);
procedure Try is
X : A_Type := …;
Y : A_Type := …;
begin
Safe_Copy (X, Y);
…
end Try;
http://libre.adacore.com
Safe_Copy (X, Y)
and not
Safe_Copy (Y, X)
You don't. That's why Ada
provides name parameters
© AdaCore under the GNU Free Documentation License
90
Ada has Named Parameters
type A_Type is …;
procedure Safe_Copy (Source : A_Type; Target : A_Type);
procedure Try is
X : A_Type := …;
Y : A_Type := …;
begin
Safe_Copy (Source => X, Target => Y);
…
end Try;
Named parameter
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
91
Avoiding Parameter Confusion in Ada
Summary: Two lines of defense
User defined types
Named parameters
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
92
Example of
C/C++/Java Type System Weakness
Signed Integer Overflow Semantics
Overflow in C/C++/Java
#include <limits.h>
void compute () {
int k = INT_MAX;
k = k + 1;
}
In C/C++ signed integer overflow is undefined, anything can happen
• All known implementations "wrap around"
In Java wrap around semantics are part of the language
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
94
Overflow in Ada
procedure Compute is
K : Integer := Integer'Last;
begin
Exception raised
K := K + 1;
at execution
end Compute;
time
EVERY time there is an integer overflow in Ada an exception is raised
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
95
Example: Overflow in Action in Ada
In GNAT you have to use the
switch -gnato to ask for
integer overflow checking
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
96
The Pernicious Effects of Wrap-Around
Semantics: A Java Example
final int RADIO_PORT = …;
void open (int port) {…}
void send (int port, byte data) {…}
void close (int port) {…}
void send_bytes (byte first_byte, byte last_byte) {
open (RADIO_PORT);
for (byte b = first_byte; b <= last_byte; b++) {
send (RADIO_PORT, b);
}
close (RADIO_PORT);
}
http://libre.adacore.com
The program to the left compiles
fine, and runs …
… But there is something wrong
with it. What ?
© AdaCore under the GNU Free Documentation License
97
Infinite Loop when last_byte == 127
Two problems:
Wrap around semantics of type byte
• When last_byte = b = 127 we execute the loop, we do b++ and b wraps to -128
There is no real for loop instruction in C/C++/Java
for (x; y; z) {…}
• Means
x; while (y) { …; z; }
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
98
The Ada Version is Safe
type Port is range 0 .. 255;
type Byte is range -128 .. 127;
RADIO_PORT : constant Port := …;
procedure Open (P : Port);
procedure Send (P : Port; B : Byte);
procedure Close (P : Port);
procedure Send_Bytes (First : Byte; Last : Byte) is
begin
Open (RADIO_PORT);
for B in First .. Last loop
Send (RADIO_PORT, B);
end loop;
Close (RADIO_PORT);
end Send_Bytes;
http://libre.adacore.com
The code on the left runs fine
There is a true for loop in Ada
(unlike C/C++/Java)
© AdaCore under the GNU Free Documentation License
99
Checks and Overflows Summary
In Ada
•
•
•
•
•
Every integer overflow raises an exception in Ada
Every division by zero raises an exception in Ada
Every array index overflow raises an exception in Ada
Etc.
You can disable all the Ada checks for deployment if you wish
In Java
• Java adopted most of the Ada checks except for integer overflow which
wraps around in Java
• Cannot disable checks in Java
In C/C++
• No checks
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
100
Side Notes on Ada Types
Unsigned Integers
Ada has the choice of two sorts of integer types:
• Signed integers (an exception is raised in case of an overflow)
• Unsigned integers (wrap-around semantics)
-- Example of unsigned integers in Ada
procedure Try is
type Hash_Index is mod 1023;
H : Hash_Index := 1022;
begin
H := H + 1;
-- H is equal to zero here
end Try;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
102
Subtypes
Sometimes you want to add additional constraints to a type
without creating a new type
Ada provides the notion of subtype for that
-- Example of unsigned integers in Ada
procedure Try is
type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
subtype Working_Day is Day range Mon .. Fri;
D : Day := Mon;
WD : Working_Day;
begin
WD := D; -- This is OK
WD := Sun; -- This raises an exception
end Try;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
103
Predefined Ada Subtypes
subtype Natural is Integer range 0 .. Integer ’ Last;
subtype Positive is Natural range 1 .. Natural ’ Last;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
104
Exceptions in Ada
When a Check Fails an Exception is
Raised in Ada
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
106
Ada Predefined Exceptions
The following predefined exceptions are raised when
something goes wrong in an Ada program
Constraint_Error
http://libre.adacore.com
integer overflow, computation
error (divide by zero), array
index out of range, null pointer
dereferencing, …
Storage_Error
no more memory available
Program_Error
fundamental program error
(e.g. end of function with no
return statement)
© AdaCore under the GNU Free Documentation License
107
Creating Your Own Exceptions
procedure Checks is
Internal_Error : Exception;
procedure Foo is
begin
raise Internal_Error;
end Foo;
procedure Bar is
begin
Foo;
end Bar;
begin -- of Checks
Bar;
end Checks;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
108
What Happens at Execution Time?
procedure Checks is
Internal_Error : Exception;
procedure Foo is
begin
raise Internal_Error; 3
end Foo;
2
procedure Bar is
begin
Foo;
end Bar;
begin -- of Checks
Bar;
end Checks;
http://libre.adacore.com
Exception
raised
© AdaCore under the GNU Free Documentation License
1
109
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
110
Displaying the Traceback
(How you Got There)
with Ada.Exceptions;
with GNAT.Traceback.Symbolic;
with Text_IO;
use Ada.Exceptions;
use GNAT.Traceback.Symbolic;
use Text_IO;
procedure Checks is
Internal_Error : Exception;
procedure Foo is
begin
raise Internal_Error;
end Foo;
procedure Bar is
begin
Foo;
end Bar;
Exception Handler
http://libre.adacore.com
begin -- of Checks
Bar;
exception
when E : others =>
Put_Line ("Raised exception : " & Exception_Name (E));
Put_Line (Symbolic_Traceback (E));
end Checks;
© AdaCore under the GNU Free Documentation License
111
-bargs: Program binder arguments:
-E: give exception tracebacks
-cargs: Compiler arguments:
-g: debugging on
-gnatl: print out a program listing
-gnato: overflow checks on
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
112
What Happens at Execution Time
with Ada.Exceptions;
with GNAT.Traceback.Symbolic;
with Text_IO;
use Ada.Exceptions;
use GNAT.Traceback.Symbolic;
use Text_IO;
procedure Checks is
Internal_Error : Exception;
b
d
http://libre.adacore.com
procedure Foo is
begin
raise Internal_Error;
end Foo;
a
procedure Bar is
begin
Foo;
end Bar;
c
begin -- of Checks
Bar;
e
exception
when E : others =>
Put_Line ("Raised exception : " & Exception_Name (E));
Put_Line (Symbolic_Traceback (E));
end Checks;
© AdaCore under the GNU Free Documentation License
113
Catching a Predefined Exception
with Text_IO; use Text_IO;
procedure Checks is
A : Integer := Integer ’ First;
begin
A := A - 1;
exception
when Constraint_Error =>
Put_Line (“Overflow occurred”);
end Checks;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
114
Catching Your Own Exceptions
with Text_IO; use Text_IO;
procedure Checks is
Internal_Error : Exception;
procedure Foo is
begin
raise Internal_Error;
end Foo;
procedure Bar is
begin
Foo;
end Bar;
begin -- of Checks
Bar;
exception
when Internal_Error =>
Put_Line (“problem occurred”);
when others =>
Put_Line (“some other exception”);
end Checks;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
115
Catching an Exception Where You Want
procedure Checks is
…
begin -- of Checks
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
end Checks;
http://libre.adacore.com
to catch some
exception in a region
of code without exiting
from the subprogram
you can use a declare block
© AdaCore under the GNU Free Documentation License
116
Example of a Declare Block
procedure Calc (A, B : Float) is
C, D : Float;
begin
…
declare
Old_C : Float := C;
begin
C := A * B;
D := C ** 2;
exception
when Constraint_Error =>
C := Old_C;
D := 0.0;
end;
…
end Calc;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
117
Array Pitfalls in C
Arrays in Ada
Ada has real arrays (1-dimensional and multi-dimensional)
Ada array can have its size determined at run-time
• Local variable length arrays are allowed in the latest C standard (C99)
Ada array bounds can be arbitrary, lower bound does not
have to start at 0
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
129
One of a Kind Arrays
procedure Compute (N : Integer) is
In Ada Arrays can have arbitrary
bounds
A : array (1 .. N) of Float;
begin
…
end Compute;
http://libre.adacore.com
The bounds can be dynamic
values
© AdaCore under the GNU Free Documentation License
130
Typed Arrays
procedure Compute (N : Integer) is
type Arr is array (Integer range <>) of Float;
A : Arr (1 .. N) := (others => 9);
B : Arr := A;
C : Arr (11 .. 20) := (1, 2, others => 0);
begin
C := A;
C (15 .. 18) := A (5 .. 8);
end Compute;
B takes its bounds from A
If C'Length /= A'Length then
Constraint_Error is raised
If A'Last < 8 then
Constraint_Error is raised
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
131
Arrays in Ada are Safe
If you try to index a non-existent arry position, a
Constraint_Error exception is raised
procedure Checks is
A : array (1 .. 100) of Integer;
begin
A (101) := 1;
end Checks;
http://libre.adacore.com
Exception raised
© AdaCore under the GNU Free Documentation License
132
Example of 1-Dim Array Attributes
Given A some array object
• A : array (10 .. 99) of Integer;
http://libre.adacore.com
A ' First
10
A ' Last
99
A ' Length
90
A ' Range
10 .. 99
© AdaCore under the GNU Free Documentation License
133
Ada Arrays are Powerful: No Need to
Pass Array Bounds as in C/C++/Java
procedure Calc is
type Vector is array (Natural range <>) of Float;
function Max (V : Vector) return Float is
M : Float := Float ’ First;
begin
for K in V ’ Range loop
if V (K) > M then
M := V (K);
end if;
end loop;
return M;
end Max;
V1 : Vector := (1.0, 2.0, 3.0); -- V'First = 0 and V'Last = 2
V2 : Vector (1 .. 100) := (1.0, 2.0, others => 5.0);
X : Float := Max (V1); -- X = 3.0
Y : Float := Max (V2); -- Y = 5.0
begin
…
end Calc;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
134
Ada String Predefined Array Type
type String is array (Positive range <>) of Character;
R : String (1 .. 10);
S : String := (‘H’, ‘e’, ‘l’, ‘l’, ‘o’);
T : String := “Hello”;
Q : String := S & “ “ & T & “ you”;
-- Q = "Hello Hello you"
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
135
Records and Pointers in Ada
Record Types
type Date is record
Day : Positive range 1 .. 31;
Month : Positive range 1 .. 12;
Year : Integer;
end record;
D : Date := (3, 9, 1975);
A : Date := (Day => 31, Month => 12, Year => 1999);
B : Date := A;
Y : Integer := B . Year;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
137
Pointers and Records
type Node;
type Node_Ptr is access Node;
type Node is record
D : Date := (1, 1, 1900);
Next : Node_Ptr;
end record;
P1 : Node_Ptr := new Node;
P2 : Node_Ptr := new Node ’ ((3, 9, 1975), P1);
3
9
1975
1
1
1900
null
Memory
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
138
N : Node := ((31, 12, 1999), null);
P3 : Node_Ptr := new Node ’ (N);
31
12
1999
null
Memory
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
139
Accessing Record Fields: A Simple Rule
If P is a pointer to a record then
P.all
points to the WHOLE record
P.Field
points to Field in the record
Note: P.Field
is the same as
P.all.Field
type Node is record
D : Date := (1, 1, 1900);
Next : Node_Ptr;
end record;
P : Node_Ptr := new Node;
A_Date : Date
:= P.D;
A_Node : Node_Ptr := P.Next;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
140
Parametrized Records: Discriminants
type Q_Array (Positive range <>) of Integer;
type Queue (Max_Size : Positive) is record
First : Positive := 1;
Last : Positive := 1;
Size : Natural := 0;
Q : Q_Array (1 .. Max_Size);
end record;
X : Queue (4); -- X.Max_Size = 4
Y : Queue;
http://libre.adacore.com
Compilation Error
Value for Max_Size missing
© AdaCore under the GNU Free Documentation License
141
Parameter Passing in C, Java and Ada
Three Parameter Passing Modes in Ada
in
procedure Open_File (X : String);
procedure Open_File (X : in String);
• It's the default mode, the in can be omitted
function Log (X : Float)
• Inside the procedure or function X is a constant
initialized by the value of the actual parameter
return Float;
function Log (X : in Float) return Float;
• Functions can only have parameters of mode i
in out
procedure Increment (X : in out Float);
• Inside the procedure X is a variable initialized by
the value of the actual parameter
• The actual parameter is updated with the last
value of X when the procedure terminates.
out
procedure Copy (Y : Float; X : out Float);
• Inside the procedure X is an uninitialized variable
• The actual parameter is updated with the last
value of X when the procedure terminates
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
147
Example: Mode "in"
function Log (X : Float) return Float is
begin
X := 1.0;
Compilation error
…
X is a constant inside Log
end Log;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
148
Example: Mode "in out"
procedure A_Test is
Note: In Ada You can nest
functions & procedures
procedure Increment (X : in out Float) is
begin
X := X + 1.0;
end Increment;
Value : Float := 9.0;
begin
-- Value = 9.0 here
Increment (Value);
-- Value = 10.0 here
end A_Test;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
149
Example: Mode "out"
procedure A_Test is
procedure Copy (Y : Float; X : out Float) is
begin
X := Y;
end Copy;
Value : Float;
begin
-- Value is uninitialized here
Copy (10.0, Value);
-- Value = 10.0 here
end A_Test;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
150
Ada Offers a Comprehensive Scheme
for Safe Low-Level Programming
PSW Example
System Mask
0
Protection
Key
unused
7
8
11
12
15 16
Machine
State
unused
19 20
23
Represent the following program status word (PSW):
PSW is 24 bits
Bits 0 to 7 contain a 7 bit system mask
Bits 12 to 15 contain a 4 digit protection key
Bits 20 to 24 contain the status flags of the machine for the program
•
The fours flags are called: A, M, W and P
All other bits are unused
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
152
The PSW Type in Ada
System Mask
0
7
type State
type Byte_Mask
type State_Mask
Protection
Key
unused
8
11
12
Machine
State
unused
15 16
19 20
23
is (A, M, W, P);
is array (0 .. 7) of Boolean;
is array (State) of Boolean;
type Program_Status_Word is record
System_Mask
: Byte_Mask;
Protection_Key
: Integer range 0 .. 15;
Machine_State
: State_Mask;
end record;
for Program_Status_Word use record
System_Mask
at 0 range 0 .. 7;
Protection_Key
at 0 range 12 .. 15;
Machine_State
at 0 range 20 .. 23;
end record;
for Program_Status_Word ' Size use 3 * System.Storage_Unit;
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
153
Accessing PSW Fields in Ada is …
Simple
…
type Program_Status_Word is record
System_Mask
: Byte_Mask;
Protection_Key
: Integer range 0 .. 15;
Machine_State
: State_Mask;
end record;
…
Safe
Efficient
PSW
: Program_Status_Word := …;
Key
: Integer
M_State : Boolean
Mask_3 : Boolean
http://libre.adacore.com
:= PSW . Protection_Key;
:= PSW . Machine_State (M);
:= PSW . System_Mask (3);
© AdaCore under the GNU Free Documentation License
154
There is more in Ada to help with safe and efficient
low-level programming than just the previous example
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
155
Summary: Better Safe than Sorry
Language Safety and Security
unsafe assembly
http://libre.adacore.com
C
C++
Java
© AdaCore under the GNU Free Documentation License
Ada
safe
161
Summary
A good programming language
• Encourages the writing of correct software
• Helps in detecting errors
C/C++/Java
• Encourage the writing of concise code not correct software
• C/C++ provide no help in detecting errors
• Java provides some help
Ada (and other languages such as Eiffel)
• Encourage the writing of correct software
• Provide help in detecting errors
http://libre.adacore.com
© AdaCore under the GNU Free Documentation License
162