Course Ada Advanced
Course Ada Advanced
Course Ada Advanced
Mario Blunk
Buchfinkenweg 3
99097 Erfurt / Germany
Email info@blunk-electronic.de
Web www.blunk-electronic.de
Doc. Vers. 2018-11-22
About Ada
https://en.wikibooks.org/wiki/Ada_Programming :
Be courageous !
For Engineers: Why Ada ?
Ada is beautiful !
strong and safe type system (avoids mixing miles with kilometers,
Euros with Rubles, ...)
7. generics
procedure environment_1 is
begin
put_line ("argument count: " &
natural'image(argument_count) );
end if;
end environment_1;
Command Line Arguments #2
with ada.command_line; use ada.command_line;
procedure environment_2 is
i : integer;
f : float;
begin
-- i := argument(1); -- does not compile
i := integer'value( argument(1) );
put_line("argument 1: " & integer'image(i));
f := float'value ( argument(2) );
put_line("argument 2: " & float'image(f));
end environment_2;
https://www.adacore.com/gems/gem-138-gnatcoll.command-line
Exit Status
with ada.command_line;
use ada.command_line;
procedure environment_3 is
e : exit_status := failure;
begin
if argument_count > 0 then
put_line ("everything fine");
e := success;
else
put_line ("error: arguments missing !");
end if;
set_exit_status (e);
end environment_3;
Environment Variables
with ada.environment_variables;
use ada.environment_variables;
procedure environment_4 is
begin
if exists ( "HOME" ) then
put_line ("my home dir is: " & value("HOME") );
else
put_line ("warning: no home directory !");
end if;
clear ("DUMMY");
end environment_4;
Directory & File Operations #1
with ada.directories; use ada.directories;
procedure directory_and_file_ops_1 is
begin
put_line ("the current working directory is: " &
current_directory);
end directory_and_file_ops_1;
Directory & File Operations #2
with ada.directories; use ada.directories;
procedure directory_and_file_ops_2 is
begin
put_line (full_name (file)); -- absolute path
put_line (containing_directory (file));
s := size(file);
put_line (file_size'image(s) & " bytes");
end directory_and_file_ops_2;
Directory & File Operations #3
with ada.text_io; use ada.text_io;
with ada.directories; use ada.directories;
procedure directory_and_file_ops_3 is
handle : ada.text_io.file_type;
begin
create (
file => handle,
mode => out_file, -- data will go into the file
name => compose (
containing_directory => current_directory,
name => "dummy",
extension => "txt" )
);
Ada.Directories
files: a-direct.ads
Ada.Text_IO
files: a-textio.ads
begin
b := "2020";
end string_processing_1;
Fixed Strings #2
with ada.strings.fixed; use ada.strings.fixed;
procedure string_processing_1a is
a : string (4..10) := "Ada2012";
b : string (1..20) := (20 * '-');
begin
put_line (positive'image(a'length));
put_line (positive'image(a'first));
put_line (positive'image(a'last));
put_line (character'image(a(5)));
a(9) := '2';
end string_processing_1a;
Bounded Strings #1
with ada.strings.bounded; use ada.strings.bounded;
procedure string_processing_2 is
package type_universal_string is new
generic_bounded_length(100);
b := to_bounded_string("2012");
put_line (a & " " & to_string(b));
end string_processing_2;
Bounded Strings #2
a : type_universal_string.bounded_string :=
to_bounded_string("Ada2012");
begin
put_line (positive'image( length(a) ));
put_line (character'image( element(a,1) ));
put_line (character'image( element(a, length(a)) ));
replace_element ( a, 6, '3' );
put_line (slice (a, 4, 7 ));
put_line (type_universal_string.to_string(a));
end string_processing_2a;
Unbounded Strings #1
with ada.strings.unbounded; use ada.strings.unbounded;
procedure string_processing_3 is
begin
b := to_unbounded_string("95");
put_line (a & " " & to_string(b));
b := to_unbounded_string("2012");
put_line (a & " " & to_string(b));
end string_processing_3;
Unbounded Strings #2
a : unbounded_string := to_unbounded_string("Ada2012");
begin
put_line (positive'image( length(a)));
put_line (character'image( element(a,1) ) );
put_line (character'image( element(a, length(a)) ) );
replace_element ( a, 6, '3' );
put_line (slice (a, 4, 7 ));
put_line (to_string(a));
end string_processing_3a;
try playing with a dervied type like: type my_unbounded is new unbounded_string;
String Type Conversion
package type_us1 is new generic_bounded_length(20);
use type_us1;
package type_us2 is new generic_bounded_length(10);
use type_us2;
end string_processing_4;
Character Sets #1
with ada.strings; use ada.strings;
with ada.strings.maps; use ada.strings.maps;
procedure character_sets_1 is
end character_sets_1;
Character Mapping #1
with ada.strings; use ada.strings;
with ada.strings.maps; use ada.strings.maps;
with ada.strings.bounded; use ada.strings.bounded;
procedure character_mapping_1 is
end character_mapping_1;
Controlled Types #1
procedure parameterized_types_1 is
begin
c.manufacturer := to_unbounded_string("Vauxhall");
c.door_count := 3;
c : type_car;
d : type_car_with_special_licence;
begin
c := type_car (d);
d := (c with special_licence => false);
end tagged_types_1;
Tagged Types #2
procedure tagged_types_2 is
type type_car is tagged
record
manufacturer : unbounded_string;
door_count : positive;
end record;
c : type_car;
d : type_car_with_special_licence (seat_count => 10);
begin
c := type_car (d);
d := (c with seat_count => 10, special_licence => false);
end tagged_types_2;
Dynamic dispatching #1
package objects is
type type_point is tagged record
x, y : integer;
end record;
end objects;
Dynamic dispatching #3
with objects;use objects;
procedure tagged_types_3 is
invalid/missing arguments
insufficient memory
division by zero
...
Exceptions: Constraint_Error #1
procedure exceptions_1 is
begin
end exceptions_1;
Exceptions: Constraint_Error #2
procedure exceptions_2 is
-- Exception handler:
exception
when constraint_error =>
put_line ("ERROR: Array index invalid !");
end exceptions_2;
Exceptions: Constraint_Error #3
procedure exceptions_3 is
p, q, r : natural := 0;
begin
r := p / q; -- Division by zero.
-- Causes a warning at compile time and
-- a CONSTRAINT_ERROR at run time.
-- Program control passed to exception handler.
-- Exception handler:
exception
when constraint_error =>
put_line ("ERROR: Division by zero !");
end exceptions_3;
Predefined Exceptions
predefined exceptions:
Program_Error
Storage_Error
Tasking_Error
Exception Handler #1
procedure exceptions_4 is
data_format_error : exception; -- user specific exception
operator_error : exception; -- user specific exception
begin
-- We intentionally raise exceptions to demonstrate the
-- exception handler:
-- raise constraint_error;
-- raise storage_error;
-- raise data_format_error;
raise operator_error;
exception
when constraint_error =>
put_line ("Constraint error occured !");
when storage_error =>
put_line ("Storage error occured !");
when data_format_error =>
put_line ("Data format error occured !");
when others =>
put_line ("Other error occured !");
end exceptions_4;
Exception Handler #2
procedure exceptions_5 is
operator_error : exception; -- user specific exception
program_position : natural := 0;
begin
--raise operator_error;
program_position := 10; --raise operator_error;
program_position := 30; raise operator_error;
put_line("Everything fine."); -- skipped on exception
exception
when constraint_error =>
put_line ("Constraint error occured !");
when operator_error =>
put ("Operator error ! ");
case program_position is
when 0 => put_line ("Missing arguments");
when 10 => put_line ("Invalid argument given.");
when others => put_line ("Contact administrator !");
end case;
when others =>
put_line ("Other error occured !");
end exceptions_5;
Exception Handler #3
with ada.exceptions; use ada.exceptions;
procedure exceptions_6 is
operator_error : exception; -- user specific exception
begin
raise operator_error with "Wrong key pressed !";
--raise constraint_error;
exception
when event:
operator_error =>
put_line(exception_information(event));
when constraint_error =>
put_line ("Constraint error occured !");
when others =>
put_line ("Other error occured !");
end exceptions_6;
Exception Handler #4
exceptions_6a.adb
Exception Propagation
procedure exception_prop_1 is
operator_error : exception; -- user specific exception
procedure request_operator_input is
begin
null; -- assume operator input here
raise operator_error; -- we intentionally raise an
-- exception
end request_operator_input;
begin
request_operator_input;
put_line("Everything fine."); -- skipped on exception
exception
when operator_error =>
put_line ("Operator error occured !");
when others =>
put_line ("Other error occured !");
end exception_prop_1;
Containers
When objects are to be stored, sorted and queried ...
procedure cont_doubly_linked_list_1 is
package type_my_list is new doubly_linked_lists(natural);
l : type_my_list.list;
c : type_my_list.cursor;
n : natural;
begin
type_my_list.append(l,7); -- append object '7' to list 'l'
type_my_list.append(l,9); -- append object '9' to list 'l'
use type_my_list;
l : type_my_list.list;
c : type_my_list.cursor;
begin
end cont_doubly_linked_list_3;
Doubly Linked Lists #4
Ada.Containers.Doubly_Linked_Lists
file: a-cdlili.ads
procedure cont_vectors_1 is
v : type_my_vector.vector;
n : natural;
begin
type_my_vector.append(v,7); -- add first object
type_my_vector.append(v,9); -- add next object
Ada.Containers.Vectors
file: a-convec.ads
m : type_my_map.map;
n : natural;
begin
type_my_map.insert(m,123,7); -- ins. object '7' with key '123'
type_my_map.insert(m,788,99); -- ins. obj. '99' with key '788'
m : type_my_map.map;
n : natural;
begin
type_my_map.insert(m,'A',7); -- ins. object '7' with key 'A'
type_my_map.insert(m,'X',99); -- ins. obj. '99' with key 'X'
m : type_my_map.map;
c : type_my_map.cursor;
begin
insert(m,'A',7); -- insert object '7' with key 'A'
insert(m,'X',99); -- insert object '99' with key 'X'
insert(m,'Z',4); -- insert object '99' with key 'X'
c := first (m);
end cont_maps_3;
Ordered Maps #4
procedure cont_maps_4 is
use type_my_map;
m : type_my_map.map;
c : type_my_map.cursor;
inserted : boolean; -- goes true if the object got inserted
begin
insert ( -- insert object '7' with key 'A'
container => m,
key => 'A',
new_item => 7,
position => c,
inserted => inserted
);
procedure change (
name : in character;
thing : in out natural) is
begin
thing := 100;
end change;
begin -- cont_maps_5
insert ... -- see cont_maps_4
if inserted then
update_element (
container => m,
position => c,
process => change'access);
end if;
https://github.com/Blunk-electronic/ada_training/blob/master/src/cont_maps_6/cont_maps_6.adb
Ordered Maps #7
Ada.Containers.Ordered_Maps
file: a-coorma.ads
procedure cont_ordered_sets_1 is
package type_my_list is new ordered_sets (natural);
use type_my_list;
l : type_my_list.set;
c : type_my_list.cursor;
begin
insert(l,7); -- append object '7' to list 'l'
insert(l,2); -- append object '9' to list 'l'
c := l.first;
put_line (natural'image (element (c))); -- 2
next (c);
put_line (natural'image (element (c))); -- 7
end cont_ordered_sets_1;
Indefinite Containers #1
Handling indefinite containers is quite similar to definite containers.
https://github.com/Blunk-electronic/ada_training/blob/master/src/cont_doubly_linked_list_2b/
cont_doubly_linked_list_2b.adb
Library Units: Why ?
target
executable binary file
Library Units: Makefile #2
TARGET = tagged_types_3
VPATH = include
OBJS = $(TARGET).o objects.o
PREFIX = $(HOME)
BINDIR = $(PREFIX)/bin
# compile
.adb.o:
gcc -c -gnat2012 $< -I $(VPATH)
.SUFFIXES: .adb .o
# link
$(TARGET): $(OBJS)
gnatbind -x $(TARGET).ali; gnatlink $(TARGET).ali
install:
install -D $(TARGET) $(BINDIR)/$(TARGET)
uninstall:
-rm $(BINDIR)/$(TARGET)
clean:
rm *.o *.ali $(TARGET)
Library Units: GPRbuild
project tagged_types_3 is
package simple_library is
end simple_library;
Library Units: Body
-------------------------------
-- --
-- simple_library --
-- --
-- B o d y --
-------------------------------
end simple_library;
Library Units: Parent
with simple_library; -- use simple_library;
procedure library_units_1 is
begin
end library_units_1;
Library Units: Include
$> ls -l
total 8
lrwxrwxrwx 1 hans users 6 2017-01-06 09:08 include -> ../lib
-rw-r--r-- 1 hans users 244 2017-01-06 09:08 library_units_1.adb
-rw-r--r-- 1 hans users 392 2017-01-06 09:14 Makefile
ln -s target target_name
need-to-know principle
coding safety
Private Types: Why ?
procedure types_private_1 is
type wealth is record
cash, estate, total : natural := 0;
end record;
w, p : wealth;
begin
p.cash := 8; p.estate := 4; w := w + p;
put_line ("total :" & natural'image(w.total));
w.total := 100; -- Compiles, yet it's a lie !
end types_private_1;
Private Types: Specificaton
package library_with_private_types is
private
type wealth is record
cash : natural := 0;
estate : natural := 0;
total : natural := 0;
end record;
end library_with_private_types;
Private Types Body
procedure types_private_2 is
w, p : wealth;
begin
w := take_money(8);
p := take_estate(4);
w := w + p;
end types_private_2;
Generics
generic subprograms
begin
put_line("x:" & natural'image(x) & " y:" & natural'image(y));
swap_natural(x,y);
put_line("x:" & natural'image(x) & " y:" & natural'image(y));
end generics_1;
Generics as Package: Spec
---------------------------------
-- --
-- library_with_generic --
-- --
-- S p e c --
---------------------------------
package library_with_generic is
generic
type item is private;
procedure swap_items(x,y : in out item);
end library_with_generic;
Generics as Package: Body
----------------------------------
-- --
-- library_with_generic --
-- --
-- B o d y --
----------------------------------
end library_with_generic;
Generics as Package: Parent #1
with library_with_generic;
procedure generics_2 is
use library_with_generic;
procedure swap_natural is new swap_items(item => natural);
x : natural := 10;
y : natural := 7;
begin
swap_natural(x,y);
end generics_2;
Generics as Package: Parent #2
with ada.strings.unbounded; use ada.strings.unbounded;
with library_with_generic;
procedure generics_3 is
use library_with_generic;
procedure swap_unbounded is new swap_items
(item => unbounded_string);
x : unbounded_string := to_unbounded_string("ABC");
y : unbounded_string := to_unbounded_string("XYZ");
begin
put_line("x:" & to_string(x) & " y:" & to_string(y));
swap_unbounded(x,y);
end generics_3;
Access Types: Basics #1
with ada.text_io; use ada.text_io;
procedure types_access_1 is
begin
ai := new integer'(-10);
allocator with init value
end types_access_1;
Access Types: Basics #2
procedure types_access_2 is
-- Declare two accesses to an integer:
ai, bi : access integer;
begin
-- Create integer #1 of value -10 where ai is pointing at:
ai := new integer'(-10);
bi := ai; -- backup address of integer #1
begin
-- Create integer #1 of value -10 where ai is pointing at:
ai := new integer'(-10);
-- Create integer #2 of value -20 where ai is pointing at:
ai := new integer'(-20);
-- Change integer #2 to value -21
ai.all := -21;
begin
-- Create integer #1 of value -10 where ai is pointing at:
ai := new integer'(-10);
-- Create integer #2 of value -20 where ai is pointing at:
bi := new integer'(-20);
put_line ( integer'image(ai.all) );
end types_access_4;
Access Types: Basics #5
procedure types_access_5 is
begin
-- Create integer #1 of value -10 where ai is pointing at:
ai := new integer'(-10);
end types_access_5;
Access Types: Basics #6
procedure types_access_6 is
begin
put_line ( integer'image (i) ); -- i before manipulation
end types_access_7;
Access to Records #1
procedure access_records_1 is
-- Define an apple:
type apple is record
weight : float;
size : float;
rotten : boolean;
end record;
end access_records_1;
Access to Records #2
procedure access_records_2 is
begin
-- Call procedure say_hello via access p:
p("hello");
end access_procedures_1;
Access to Procedures #2
procedure access_procedures_2 is
-- Define an access to ANY procedure that "inouts" a number:
type type_my_access is not null access
procedure (n : in out integer);
a : integer := 3;
p : type_my_access := double'access;
begin
p(a); put_line(integer'image(a)); -- result 6
p := square'access;
p(a); put_line(integer'image(a)); -- result 36
end access_procedures_2;
Access to Functions #1
procedure access_functions_1 is
-- Define an access to ANY function takes and returns an
-- integer:
type type_my_access is access
function ( i : in integer) return integer;
begin
-- Call function double via access p:
put_line( integer'image( p(4) ) );
end access_functions_1;
Access to Functions #2
procedure access_functions_2 is
-- Define an access to ANY function takes and returns an integer:
type type_my_access is access
function ( i : in integer) return integer;
p : type_my_access := double'access;
begin
put_line( integer'image( p(4) ) ); -- result 8
p := square'access;
put_line( integer'image( p(4) ) ); -- result 16
end access_functions_2;
Launching external programs #1
with gnat.os_lib; use gnat.os_lib;
procedure call_external_program_1 is
result : natural;
begin
spawn -- blocking call of command „ls -l“
(
program_name => "/bin/ls",
args => (1=> new string'("-l")),
output_file_descriptor => standout,
return_code => result
);
procedure call_external_program_2 is
pid : gnat.os_lib.process_id;
begin
pid := non_blocking_spawn
(
program_name => "/bin/ls",
args => (1=> new string'("-l")),
output_file_descriptor => standout
);
end call_external_program_2;
Launching external programs #3
with gnat.os_lib; use gnat.os_lib;
procedure call_external_program_3 is
result : natural;
begin
-- blocking call
result := system ("/bin/sleep 2 && /bin/ls" & ASCII.NUL);
end call_external_program_3;
References
(1)http://www.adaic.org
(2)https://en.wikibooks.org/wiki/Ada_Programming
(3)https://github.com/Blunk-electronic/ada_training
(4)http://www.blunk-electronic.de/ada.html