From 1c76f20b5c526537cb9593045e297f46326ee498 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 13 Oct 2024 04:45:45 +0200 Subject: [PATCH 01/13] Editorial change: removing todo item --- .../advanced-ada/parts/data_types/records.rst | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/content/courses/advanced-ada/parts/data_types/records.rst b/content/courses/advanced-ada/parts/data_types/records.rst index f5b1e5b0d..59dd19992 100644 --- a/content/courses/advanced-ada/parts/data_types/records.rst +++ b/content/courses/advanced-ada/parts/data_types/records.rst @@ -2918,19 +2918,15 @@ In this example, :ada:`Arr_Sub` and :ada:`Rec_Sub` are unconstrained subtypes. - :arm:`3.2 Types and Subtypes <3-2>` -.. - TO BE DONE: +Variant parts +------------- - Variant parts - ------------- +.. admonition:: In the Ada Reference Manual - .. admonition:: In the Ada Reference Manual + - :arm:`3.8.1 Variant Parts and Discrete Choices <3-8-1>` - - :arm:`3.8.1 Variant Parts and Discrete Choices <3-8-1>` - .. todo:: - - Complete section! From 1df45640ce920cc4408b1ad1d512ba51d9379008 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 13 Oct 2024 04:46:13 +0200 Subject: [PATCH 02/13] Editorial change: adding anchor --- content/courses/advanced-ada/parts/data_types/records.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/courses/advanced-ada/parts/data_types/records.rst b/content/courses/advanced-ada/parts/data_types/records.rst index 59dd19992..fb5f8dc12 100644 --- a/content/courses/advanced-ada/parts/data_types/records.rst +++ b/content/courses/advanced-ada/parts/data_types/records.rst @@ -2918,6 +2918,8 @@ In this example, :ada:`Arr_Sub` and :ada:`Rec_Sub` are unconstrained subtypes. - :arm:`3.2 Types and Subtypes <3-2>` +.. _Adv_Ada_Variant_Records: + Variant parts ------------- From 5e4cad8dcc1a15e16c22b34bc1e5caaf1de6b53c Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 13 Oct 2024 04:46:23 +0200 Subject: [PATCH 03/13] Editorial change: adding anchor --- content/courses/advanced-ada/parts/data_types/aggregates.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/courses/advanced-ada/parts/data_types/aggregates.rst b/content/courses/advanced-ada/parts/data_types/aggregates.rst index 6721877eb..e3be74b58 100644 --- a/content/courses/advanced-ada/parts/data_types/aggregates.rst +++ b/content/courses/advanced-ada/parts/data_types/aggregates.rst @@ -526,6 +526,8 @@ This code compiles because :ada:`<>` uses the appropriate default value of each component. +.. _Adv_Ada_Aggregates_Record_Discriminants: + Record discriminants ~~~~~~~~~~~~~~~~~~~~ From 2e11e43ec5bd2423099f1369f824706f8c2cbdea Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 13 Oct 2024 04:46:46 +0200 Subject: [PATCH 04/13] Editorial change: adding anchor --- .../advanced-ada/parts/data_types/types_representation.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/content/courses/advanced-ada/parts/data_types/types_representation.rst b/content/courses/advanced-ada/parts/data_types/types_representation.rst index 09a7b46d1..950b4d7f7 100644 --- a/content/courses/advanced-ada/parts/data_types/types_representation.rst +++ b/content/courses/advanced-ada/parts/data_types/types_representation.rst @@ -2030,6 +2030,9 @@ when we know that information stored in memory might be corrupted. - :arm22:`13.9.2 The Valid Attribute <13-9-2>` + +.. _Adv_Ada_Unchecked_Union: + Unchecked Union --------------- From 65c94f5751efa0d8b940e80e98ab01f216053e84 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 13 Oct 2024 04:47:11 +0200 Subject: [PATCH 05/13] Adding reference to new section on variant parts --- .../advanced-ada/parts/data_types/types_representation.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/courses/advanced-ada/parts/data_types/types_representation.rst b/content/courses/advanced-ada/parts/data_types/types_representation.rst index 950b4d7f7..eb657d16e 100644 --- a/content/courses/advanced-ada/parts/data_types/types_representation.rst +++ b/content/courses/advanced-ada/parts/data_types/types_representation.rst @@ -2040,6 +2040,8 @@ We've introduced variant records back in the :ref:`Introduction to Ada course `. In simple terms, a variant record is a record with discriminants that allows for changing its structure. Basically, it's a record containing a :ada:`case`. +(We talk again about :ref:`variant records ` in +another chapter.) The :ada:`State_Or_Integer` declaration in the :ada:`States` package below is an example of a variant record: From 91bc2b64a145022b0a7b7cee5840694004406d43 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 13 Oct 2024 04:47:34 +0200 Subject: [PATCH 06/13] Adding reference to section on variant parts --- content/courses/advanced-ada/parts/data_types/records.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/content/courses/advanced-ada/parts/data_types/records.rst b/content/courses/advanced-ada/parts/data_types/records.rst index fb5f8dc12..03dd18446 100644 --- a/content/courses/advanced-ada/parts/data_types/records.rst +++ b/content/courses/advanced-ada/parts/data_types/records.rst @@ -2322,6 +2322,10 @@ constrained, however, the discriminants must match. If we assign an object to of that procedure: we check the state of the :ada:`Extended` discriminant before assigning an object to the :ada:`R` parameter. +Note that the :ada:`Simple_Record` type has a +:ref:`variant part `. We discuss this topic later on +in this chapter. + The :ada:`Using_Constrained_Attribute` procedure below declares two objects of :ada:`Simple_Record` type: :ada:`R1` and :ada:`R2`. Because the :ada:`Simple_Record` type has a default value for its discriminant, we can From fc30d6d55bd4b996dd2b93f5b97a2c27083a7408 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 13 Oct 2024 04:48:22 +0200 Subject: [PATCH 07/13] Adding section on variant parts --- .../advanced-ada/parts/data_types/records.rst | 667 ++++++++++++++++++ 1 file changed, 667 insertions(+) diff --git a/content/courses/advanced-ada/parts/data_types/records.rst b/content/courses/advanced-ada/parts/data_types/records.rst index 03dd18446..4a6377ca5 100644 --- a/content/courses/advanced-ada/parts/data_types/records.rst +++ b/content/courses/advanced-ada/parts/data_types/records.rst @@ -2927,13 +2927,680 @@ In this example, :ada:`Arr_Sub` and :ada:`Rec_Sub` are unconstrained subtypes. Variant parts ------------- +We've introduced variant records back in the +:ref:`Introduction to Ada course `. +In simple terms, a variant record is a record with discriminants that allows +for changing its structure. Basically, it's a record containing a :ada:`case` +statement. For example: + +.. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Simple_Device + + package Devices is + + type Device_State is + (Off, On); + + type Device_Info is + record + V : Float; + end record; + + type Device (State : Device_State) is + record + case State is + when Off => + null; + when On => + Info : Device_Info; + end case; + end record; + + end Devices; + +The :ada:`Device` type from this example has a variant part. Depending on the +value of the :ada:`State` discriminant, it can be either a null record (when +:ada:`State` is :ada:`Off`) or have the :ada:`Info` component (when +:ada:`State` is :ada:`On`). + +Let's look at a test application for the :ada:`Devices` package: + +.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Simple_Device + :class: ada-expect-compile-error + + with Devices; use Devices; + + procedure Show_Device is + D : Device; + D_Off : Device (Off); + D_On : Device (On); + begin + D := D_Off; + -- OK! + + D := D_On; + -- OK! + + D_Off := D_On; + -- ^^^^ + -- CONSTRAINT_ERROR! + + D_On := D_Off; + -- ^^^^^ + -- CONSTRAINT_ERROR! + end Show_Device; + +As we've discussed +:ref:`previously `, when we +set the values for the discriminants of a type in the object declaration, we're +constraining the objects. If the discriminants of two objects don't match, the +:ada:`Constraint_Error` exception is raised at runtime because the +:ref:`discriminant check ` fails. Therefore, in the +:ada:`Show_Device` procedure, because :ada:`D_Off` and :ada:`D_On` are +constrained and have different values for the :ada:`State` discriminant, we +cannot assign them to each other. In contrast, because :ada:`D` wasn't +constrained at its declaration, we can assign objects with different +discriminants (such as :ada:`D_Off` and :ada:`D_On`) to it. + +Note that the variant part of a record can be more complex. For example, we +could have an additional discriminant and use it in the variant part: + +.. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Device_Boost + + package Devices is + + type Device_State is + (Off, On); + + type Device_Info is + record + V : Float; + end record; + + type Device (State : Device_State; + Boost : Boolean) is + record + case State is + when Off => + null; + when On => + Info : Device_Info; + case Boost is + when False => + null; + when True => + Factor : Float; + end case; + end case; + end record; + + end Devices; + +In this version of the :ada:`Devices` package, we introduced a *boost button* +as a discriminant (:ada:`Boost`) and an associated boost factor component +(:ada:`Factor`) in the variant part. + +In the remaining of this section, we discuss a couple of details about variant +records. + .. admonition:: In the Ada Reference Manual - :arm:`3.8.1 Variant Parts and Discrete Choices <3-8-1>` +Discriminant type and value coverage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The subtype of discriminants used in the variant part must be of a discrete +type |mdash| it cannot be of an access or a floating-point type, for example. +Also, all possible values of the subtype of each discriminant must be covered +in the case statement of the variant part. For example, consider the following +variant record: + +.. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Coverage + :class: ada-expect-compile-error + + package Subtype_Coverage is + + type Var_Rec (Value : Integer) is + record + case Value is + when 0 .. 100 => + I : Integer; + + -- ERROR: missing values! + end case; + end record; + + end Subtype_Coverage; + +This package cannot be compiled because, in the variant part, we're only +covering values for the :ada:`Value` discriminant in the range between 0 and +100. To fix this compilation error, we have to cover all values instead. For +example: + +.. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Coverage + + package Subtype_Coverage is + + type Var_Rec (Value : Integer) is + record + case Value is + when Integer'First .. -1 => + null; + when 0 .. 100 => + I : Integer; + when 101 .. Integer'Last => + null; + end case; + end record; + + end Subtype_Coverage; + +Of course, specifying all possible values can be difficult. As an alternative, +we could simplify the case statement by just using :ada:`others` as a discrete +choice that encompasses all values that haven't been specified elsewhere in the +case statement: + +.. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Coverage + + package Subtype_Coverage is + + type Var_Rec (Value : Integer) is + record + case Value is + when 0 .. 100 => + I : Integer; + when others => + null; + end case; + end record; + + end Subtype_Coverage; + +By using :ada:`when others => ...` in this last example, we ensure that all +values have been covered. + + +Record size +~~~~~~~~~~~ + +When declaring an object, the values we select for the discriminants related to +the variant part have an impact on the overall size of that object |mdash| in +fact, it may be smaller or bigger depending on this selection. Let's see an +example: + +.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Size + + package Variant_Records is + + type Simple_Record + (Extended : Boolean := False) is + record + V : Integer; + case Extended is + when False => + null; + when True => + V_Float : Float; + end case; + end record; + + end Variant_Records; + + with Ada.Text_IO; use Ada.Text_IO; + + with Variant_Records; use Variant_Records; + + procedure Show_Variant_Rec_Size is + SR_No_Ext : Simple_Record + (Extended => False); + SR_Ext : Simple_Record + (Extended => True); + SR : Simple_Record; + begin + Put_Line ("SR_No_Ext'Size : " + & SR_No_Ext'Size'Image + & " bits"); + Put_Line ("SR_Ext'Size : " + & SR_Ext'Size'Image + & " bits"); + Put_Line ("SR'Size : " + & SR'Size'Image + & " bits"); + end Show_Variant_Rec_Size; + +As we can confirm when we run this application, the choice for the discriminant +has an impact on the size of the object. In the case of the :ada:`SR_No_Ext` +object, setting the :ada:`Extended` discriminant to :ada:`False` excludes the +:ada:`V_Float` component. For the :ada:`SR_Ext` object, on the other hand, we +include the :ada:`V_Float` component. Therefore, on a typical PC, the size of +:ada:`SR_No_Ext` is 8 bytes (4 bytes for the :ada:`Extended` discriminant and +4 bytes for the :ada:`V` component), while the size of :ada:`SR_Ext` is 12 +bytes (i.e., additional 4 bytes for the :ada:`V_Float` component). + +In the case of :ada:`SR`, because the object isn't constrained, the size of the +object is 12 bytes on a typical PC |mdash| the same size as :ada:`SR_Ext`. This +is because :ada:`SR` has to account for the case when all components must be +available, even though the :ada:`Extended` discriminant is set to :ada:`False` +by default. Remember that an assignment such as :ada:`SR := SR_Ext` is valid, +so enough memory must be available to ensure that the assignment is performed +correctly. + +This principle applies to more complicated variant records. For example: + +.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Size + + package Variant_Records is + + type Simple_Record + (Extended : Boolean := False; + Extended_2 : Boolean := False) is + record + V : Integer; + case Extended is + when False => + case Extended_2 is + when False => + null; + when True => + V_Int_2 : Integer; + V_Int_3 : Integer; + end case; + when True => + V_Float : Float; + case Extended_2 is + when False => + null; + when True => + V_Float_2 : Float; + end case; + end case; + end record; + + end Variant_Records; + + with Ada.Text_IO; use Ada.Text_IO; + + with Variant_Records; use Variant_Records; + + procedure Show_Variant_Rec_Size is + SR : Simple_Record; + begin + Put_Line ("SR'Size : " + & SR'Size'Image + & " bits"); + end Show_Variant_Rec_Size; + +In this example, the size of :ada:`SR` is 16 bytes on a typical PC. This +accounts for 4 bytes for the discriminants :ada:`Extended` and +:ada:`Extended_2`, and 4 bytes for each of the 3 components that are being +taken into account for the worst case: + +- components :ada:`V`, :ada:`V_Int_2` and :ada:`V_Int_3` when we set + :ada:`Extended => False, Extended_2 => True`; + +- components :ada:`V`, :ada:`V_Float` and :ada:`V_Float_2` when we set + :ada:`Extended => True, Extended_2 => True`. + +Note that a memory block is shared between the :ada:`V_Int_2` and +:ada:`V_Int_3` components from the first worst case, and :ada:`V_Float` and the +:ada:`V_Float_2` components from the second worst case. As we can see, the +compiler will typically optimize the size of a record as much as possible by +assessing which components are really needed for the worst case. + +Also, as we discussed previously, we can use +:ref:`unchecked unions ` in combination with variant +records, which has an impact on the object size. + + +Ensuring valid information +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We can use variant parts to prevent invalid information from being used. Let's +look again at the :ada:`Device` type from the previous code example: + +.. code-block:: ada + + type Device (State : Device_State) is + record + case State is + when Off => + null; + when On => + Info : Device_Info; + end case; + end record; + +For the sake of this example, we could say that a device that is turned off +doesn't have any valuable information. Therefore, the device information stored +in the :ada:`Info` component of the :ada:`Device` type is only valid if the +device is turned on. Thus, if the device is turned off (i.e., +:ada:`Device_State = Off`), we should prevent the application from processing +device information that is probably incorrect. Let's extend the previous code +example to accommodate this requirement: + +.. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Device + + package Devices is + + type Device_State is + (Off, On); + + type Device + (State : Device_State := Off) is + private; + + procedure Turn_Off (D : in out Device); + + procedure Turn_On (D : in out Device); + + type Device_Info is + record + V : Float; + end record; + + function Read_Info (D : Device) + return Device_Info; + + private + + type Device (State : Device_State := Off) is + record + case State is + when Off => + null; + when On => + Info : Device_Info; + end case; + end record; + + Device_Off : constant Device := + (State => Off); + + Device_On : constant Device := + (State => On, + others => <>); + + end Devices; + + package body Devices is + + procedure Turn_Off (D : in out Device) is + begin + D := Device_Off; + end Turn_Off; + + procedure Turn_On (D : in out Device) is + begin + D := Device_On; + end Turn_On; + + function Read_Info (D : Device) + return Device_Info is + (D.Info); + + end Devices; + +Let's create a test application called :ada:`Show_Device` that makes use of +this device by turning it on and off, and by retrieving information from it: + +.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Device + :class: ada-expect-compile-error + + with Devices; use Devices; + + procedure Show_Device is + D : Device; + I : Device_Info; + begin + Turn_On (D); + I := Read_Info (D); + + Turn_Off (D); + + -- The following call raises + -- an exception at runtime + -- because D is turned off. + I := Read_Info (D); + end Show_Device; + +In this example, by using the variant part, we're preventing that information +retrieved by an inappropriate call to the :ada:`Read_Info` function is used +elsewhere in the application. In fact, if the device is turned off, a call to +:ada:`Read_Info` raises the :ada:`Constraint_Error` exception because the +:ada:`Info` component isn't accessible. We see that effect in the +:ada:`Show_Device` procedure: the call to :ada:`Read_Info` *fails* (by raising +an exception) when the device has just been turned off. + +To avoid exceptions at runtime, we must check the device's state before +calling :ada:`Read_Info`: + +.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Device + + with Devices; use Devices; + + procedure Show_Device is + D : Device; + I : Device_Info; + begin + Turn_On (D); + + if D.State = On then + I := Read_Info (D); + end if; + + Turn_Off (D); + + if D.State = On then + I := Read_Info (D); + end if; + end Show_Device; + +Now, no exception is being raised anymore, as we only retrieve information from +the device when it is turned on |mdash| that is, we only call the +:ada:`Read_Info` function when the :ada:`State` discriminant of the object is +set to :ada:`On`. + + +Extending record types +~~~~~~~~~~~~~~~~~~~~~~ + +We can use variant parts as a means to extend record types. This can be viewed +as a static approach to implement type extension |mdash| similar to type +extension via tagged types, but with clear differences. + +Let's say we have a sensor, and we implement a package called :ada:`Sensors` +that interfaces with that sensor: + +.. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Sensors + + package Sensors is + + type Sensor is private; + + type Sensor_Info is + record + Info_1 : Float := 0.0; + end record; + + function Read_Info (S : Sensor) + return Sensor_Info; + + procedure Display (SI : Sensor_Info); + + private + + type Sensor is null record; + + end Sensors; + + with Ada.Text_IO; use Ada.Text_IO; + + package body Sensors is + + function Read_Info (S : Sensor) + return Sensor_Info is + ((Info_1 => 4.0)); + -- ^^^^ + -- NOTE: we're returning dummy + -- information! + + procedure Display (SI : Sensor_Info) is + begin + Put_Line ("Info_1 : " + & SI.Info_1'Image); + end Display; + + end Sensors; + +The :ada:`Sensor` type from the :ada:`Sensors` package has two subprograms: the +:ada:`Read_Info` function and the :ada:`Display` procedure. We use those +subprograms in the :ada:`Show_Sensors` procedure below: + +.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Sensors + + with Ada.Text_IO; use Ada.Text_IO; + + with Sensors; use Sensors; + + procedure Show_Sensors is + S1 : Sensor; + begin + Display (Read_Info (S1)); + end Show_Sensors; + +Now, let's assume that a new model of this sensor is available, and it has +additional features |mdash| e.g., it provides additional information to the +user. If we wanted to update the application to be able to handle this new +model of the sensor without removing support for the original model, we could +convert the :ada:`Sensor_Info` type to a tagged type and derive a +:ada:`Sensor_Info_V2` type from it. (We would probably have to implement a +:ada:`Sensor_V2` type derived from the :ada:`Sensor` type as well.) + +Alternatively, we could add a variant part to the :ada:`Sensor_Info` type to +store the additional information. For example: + +.. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Sensors + + package Sensors is + + type Sensor_Model is (Sensor_V1, + Sensor_V2); + + type Sensor + (Model : Sensor_Model := Sensor_V1) is + private; + + type Sensor_Info + (Model : Sensor_Model := Sensor_V1) is + record + Info_1 : Float := 0.0; + case Model is + when Sensor_V1 => + null; + when Sensor_V2 => + Info_2 : Float := 0.0; + end case; + end record; + function Read_Info (S : Sensor) + return Sensor_Info; + + procedure Display (SI : Sensor_Info); + + private + + type Sensor + (Model : Sensor_Model := Sensor_V1) is + null record; + + end Sensors; + + with Ada.Text_IO; use Ada.Text_IO; + + package body Sensors is + + function Read_Info (S : Sensor) + return Sensor_Info is + begin + -- Using dummy info for the information + -- returned by the function + case S.Model is + when Sensor_V1 => + return ((Model => Sensor_V1, + Info_1 => 4.0)); + when Sensor_V2 => + return ((Model => Sensor_V2, + Info_1 => 8.0, + Info_2 => 6.0)); + end case; + end Read_Info; + + procedure Display (SI : Sensor_Info) is + begin + Put_Line ("Model : " + & SI.Model'Image); + Put_Line ("Info_1 : " + & SI.Info_1'Image); + if SI.Model = Sensor_V2 then + Put_Line ("Info_2 : " + & SI.Info_2'Image); + end if; + end Display; + + end Sensors; + +In this new version of the :ada:`Sensors` package, the :ada:`Model` +discriminant was added to the :ada:`Sensor_Info` type. If the model is set to +version 2 for a specific sensor (i.e., :ada:`Model = Sensor_V2`), a new +component (:ada:`Info_2`) is available. + +The :ada:`Read_Info` and :ada:`Display` subprograms have been adapted to +take this new model into account. In the :ada:`Read_Info` function, we return +information for the newer model of the sensor. In the :ada:`Display` +procedure, we display the additional information provided by the newer model. + +Note that the original test application that makes use of the sensor +(:ada:`Show_Sensors`) doesn't require any adaptation: + +.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Sensors + + + with Ada.Text_IO; use Ada.Text_IO; + + with Sensors; use Sensors; + + procedure Show_Sensors is + S1 : Sensor; + begin + Display (Read_Info (S1)); + end Show_Sensors; + +Because we have a default value for the discriminant of the :ada:`Sensor` type, +we're essentially making the type *backwards-compatible*, so that users of this +type don't have to adapt their code after the update to the :ada:`Sensors` +package. Of course, we don't have *binary backwards-compatibility* because the +size of the type (:ada:`Sensor_Info'Size`) increases. + +Of course, in our test application, we can also use the new model of that +sensor: + +.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Sensors + + with Ada.Text_IO; use Ada.Text_IO; + + with Sensors; use Sensors; + + procedure Show_Sensors is + S1 : Sensor; + S2 : Sensor (Sensor_V2); + begin + Display (Read_Info (S1)); + Display (Read_Info (S2)); + end Show_Sensors; +In the updated version of the :ada:`Show_Sensors` procedure, we're now using +both old and new versions of the sensor. .. _Adv_Ada_Per_Object_Expressions: From 25e388a986c4505deff9396c9d3ef67fed66e3e3 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 13 Oct 2024 04:48:51 +0200 Subject: [PATCH 08/13] Adding comment about aggregates and record discriminants --- content/courses/advanced-ada/parts/data_types/records.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/content/courses/advanced-ada/parts/data_types/records.rst b/content/courses/advanced-ada/parts/data_types/records.rst index 4a6377ca5..7ae87e89a 100644 --- a/content/courses/advanced-ada/parts/data_types/records.rst +++ b/content/courses/advanced-ada/parts/data_types/records.rst @@ -2326,6 +2326,12 @@ Note that the :ada:`Simple_Record` type has a :ref:`variant part `. We discuss this topic later on in this chapter. +Note as well that, in the initialization of the :ada:`Zero_Not_Extended` and +:ada:`Zero_Extended` constants, we have to indicate the discriminant as a +component of the aggregates (e.g.: :ada:`(Extended => False, V => 0)`. We +discuss this topic in another chapter when we learn more about +:ref:`aggregates and record discriminants `. + The :ada:`Using_Constrained_Attribute` procedure below declares two objects of :ada:`Simple_Record` type: :ada:`R1` and :ada:`R2`. Because the :ada:`Simple_Record` type has a default value for its discriminant, we can From 4aa18edfa4b854303e2743ba1662ab6766342285 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 13 Oct 2024 13:32:48 +0200 Subject: [PATCH 09/13] Editorial change: correcting Sphinx class --- content/courses/advanced-ada/parts/data_types/records.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/courses/advanced-ada/parts/data_types/records.rst b/content/courses/advanced-ada/parts/data_types/records.rst index 7ae87e89a..c1d2fd22a 100644 --- a/content/courses/advanced-ada/parts/data_types/records.rst +++ b/content/courses/advanced-ada/parts/data_types/records.rst @@ -2971,7 +2971,7 @@ value of the :ada:`State` discriminant, it can be either a null record (when Let's look at a test application for the :ada:`Devices` package: .. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Simple_Device - :class: ada-expect-compile-error + :class: ada-run-expect-failure with Devices; use Devices; @@ -3351,7 +3351,7 @@ Let's create a test application called :ada:`Show_Device` that makes use of this device by turning it on and off, and by retrieving information from it: .. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Device - :class: ada-expect-compile-error + :class: ada-run-expect-failure with Devices; use Devices; From da01101fd9693f329fd709ca789abf9f07d6b04d Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 13 Oct 2024 13:33:02 +0200 Subject: [PATCH 10/13] Minor editorial change --- content/courses/advanced-ada/parts/data_types/records.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/content/courses/advanced-ada/parts/data_types/records.rst b/content/courses/advanced-ada/parts/data_types/records.rst index c1d2fd22a..db26e6986 100644 --- a/content/courses/advanced-ada/parts/data_types/records.rst +++ b/content/courses/advanced-ada/parts/data_types/records.rst @@ -3571,7 +3571,6 @@ Note that the original test application that makes use of the sensor .. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Sensors - with Ada.Text_IO; use Ada.Text_IO; with Sensors; use Sensors; From 5d7fad91804925679e0683e05bfd895bcde9029d Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 13 Oct 2024 13:33:37 +0200 Subject: [PATCH 11/13] Adding default value for the discriminant --- content/courses/advanced-ada/parts/data_types/records.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/courses/advanced-ada/parts/data_types/records.rst b/content/courses/advanced-ada/parts/data_types/records.rst index db26e6986..1ecad355f 100644 --- a/content/courses/advanced-ada/parts/data_types/records.rst +++ b/content/courses/advanced-ada/parts/data_types/records.rst @@ -2951,7 +2951,7 @@ statement. For example: V : Float; end record; - type Device (State : Device_State) is + type Device (State : Device_State := Off) is record case State is when Off => From 6a38af3da7b6c6615944a7548374233e59f9f070 Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 24 Nov 2024 17:05:45 +0100 Subject: [PATCH 12/13] Integrating suggested changes --- .../advanced-ada/parts/data_types/records.rst | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/content/courses/advanced-ada/parts/data_types/records.rst b/content/courses/advanced-ada/parts/data_types/records.rst index 1ecad355f..b024f56cb 100644 --- a/content/courses/advanced-ada/parts/data_types/records.rst +++ b/content/courses/advanced-ada/parts/data_types/records.rst @@ -2936,8 +2936,9 @@ Variant parts We've introduced variant records back in the :ref:`Introduction to Ada course `. In simple terms, a variant record is a record with discriminants that allows -for changing its structure. Basically, it's a record containing a :ada:`case` -statement. For example: +for varying its structure. Basically, it's a record containing a :ada:`case` +statement that specifies which record components exist for each discriminant +value. For example: .. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Simple_Device @@ -3045,8 +3046,8 @@ In this version of the :ada:`Devices` package, we introduced a *boost button* as a discriminant (:ada:`Boost`) and an associated boost factor component (:ada:`Factor`) in the variant part. -In the remaining of this section, we discuss a couple of details about variant -records. +In the remaining parts of this section, we discuss a couple of details about +variant records. .. admonition:: In the Ada Reference Manual @@ -3104,7 +3105,7 @@ example: Of course, specifying all possible values can be difficult. As an alternative, we could simplify the case statement by just using :ada:`others` as a discrete -choice that encompasses all values that haven't been specified elsewhere in the +choice that encompasses all values that haven't been specified earlier in the case statement: .. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Coverage @@ -3370,8 +3371,8 @@ this device by turning it on and off, and by retrieving information from it: I := Read_Info (D); end Show_Device; -In this example, by using the variant part, we're preventing that information -retrieved by an inappropriate call to the :ada:`Read_Info` function is used +In this example, by using the variant part, we're preventing information +retrieved by an inappropriate call to the :ada:`Read_Info` function from being used elsewhere in the application. In fact, if the device is turned off, a call to :ada:`Read_Info` raises the :ada:`Constraint_Error` exception because the :ada:`Info` component isn't accessible. We see that effect in the @@ -3402,7 +3403,7 @@ calling :ada:`Read_Info`: end if; end Show_Device; -Now, no exception is being raised anymore, as we only retrieve information from +Now, no exception is raised, as we only retrieve information from the device when it is turned on |mdash| that is, we only call the :ada:`Read_Info` function when the :ada:`State` discriminant of the object is set to :ada:`On`. From 921d7191a2386d758fd80f0fa96464b074992cef Mon Sep 17 00:00:00 2001 From: gusthoff Date: Sun, 24 Nov 2024 17:18:02 +0100 Subject: [PATCH 13/13] Renaming function: "Read_Info" => "Current_Info" --- .../advanced-ada/parts/data_types/records.rst | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/content/courses/advanced-ada/parts/data_types/records.rst b/content/courses/advanced-ada/parts/data_types/records.rst index b024f56cb..346cf3a94 100644 --- a/content/courses/advanced-ada/parts/data_types/records.rst +++ b/content/courses/advanced-ada/parts/data_types/records.rst @@ -3306,8 +3306,8 @@ example to accommodate this requirement: V : Float; end record; - function Read_Info (D : Device) - return Device_Info; + function Current_Info (D : Device) + return Device_Info; private @@ -3342,8 +3342,8 @@ example to accommodate this requirement: D := Device_On; end Turn_On; - function Read_Info (D : Device) - return Device_Info is + function Current_Info (D : Device) + return Device_Info is (D.Info); end Devices; @@ -3361,26 +3361,26 @@ this device by turning it on and off, and by retrieving information from it: I : Device_Info; begin Turn_On (D); - I := Read_Info (D); + I := Current_Info (D); Turn_Off (D); -- The following call raises -- an exception at runtime -- because D is turned off. - I := Read_Info (D); + I := Current_Info (D); end Show_Device; In this example, by using the variant part, we're preventing information -retrieved by an inappropriate call to the :ada:`Read_Info` function from being used +retrieved by an inappropriate call to the :ada:`Current_Info` function from being used elsewhere in the application. In fact, if the device is turned off, a call to -:ada:`Read_Info` raises the :ada:`Constraint_Error` exception because the +:ada:`Current_Info` raises the :ada:`Constraint_Error` exception because the :ada:`Info` component isn't accessible. We see that effect in the -:ada:`Show_Device` procedure: the call to :ada:`Read_Info` *fails* (by raising +:ada:`Show_Device` procedure: the call to :ada:`Current_Info` *fails* (by raising an exception) when the device has just been turned off. To avoid exceptions at runtime, we must check the device's state before -calling :ada:`Read_Info`: +calling :ada:`Current_Info`: .. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Device @@ -3393,19 +3393,19 @@ calling :ada:`Read_Info`: Turn_On (D); if D.State = On then - I := Read_Info (D); + I := Current_Info (D); end if; Turn_Off (D); if D.State = On then - I := Read_Info (D); + I := Current_Info (D); end if; end Show_Device; Now, no exception is raised, as we only retrieve information from the device when it is turned on |mdash| that is, we only call the -:ada:`Read_Info` function when the :ada:`State` discriminant of the object is +:ada:`Current_Info` function when the :ada:`State` discriminant of the object is set to :ada:`On`. @@ -3430,8 +3430,8 @@ that interfaces with that sensor: Info_1 : Float := 0.0; end record; - function Read_Info (S : Sensor) - return Sensor_Info; + function Current_Info (S : Sensor) + return Sensor_Info; procedure Display (SI : Sensor_Info); @@ -3445,8 +3445,8 @@ that interfaces with that sensor: package body Sensors is - function Read_Info (S : Sensor) - return Sensor_Info is + function Current_Info (S : Sensor) + return Sensor_Info is ((Info_1 => 4.0)); -- ^^^^ -- NOTE: we're returning dummy @@ -3461,7 +3461,7 @@ that interfaces with that sensor: end Sensors; The :ada:`Sensor` type from the :ada:`Sensors` package has two subprograms: the -:ada:`Read_Info` function and the :ada:`Display` procedure. We use those +:ada:`Current_Info` function and the :ada:`Display` procedure. We use those subprograms in the :ada:`Show_Sensors` procedure below: .. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Variant_Parts.Sensors @@ -3473,7 +3473,7 @@ subprograms in the :ada:`Show_Sensors` procedure below: procedure Show_Sensors is S1 : Sensor; begin - Display (Read_Info (S1)); + Display (Current_Info (S1)); end Show_Sensors; Now, let's assume that a new model of this sensor is available, and it has @@ -3510,8 +3510,8 @@ store the additional information. For example: end case; end record; - function Read_Info (S : Sensor) - return Sensor_Info; + function Current_Info (S : Sensor) + return Sensor_Info; procedure Display (SI : Sensor_Info); @@ -3527,8 +3527,8 @@ store the additional information. For example: package body Sensors is - function Read_Info (S : Sensor) - return Sensor_Info is + function Current_Info (S : Sensor) + return Sensor_Info is begin -- Using dummy info for the information -- returned by the function @@ -3541,7 +3541,7 @@ store the additional information. For example: Info_1 => 8.0, Info_2 => 6.0)); end case; - end Read_Info; + end Current_Info; procedure Display (SI : Sensor_Info) is begin @@ -3562,8 +3562,8 @@ discriminant was added to the :ada:`Sensor_Info` type. If the model is set to version 2 for a specific sensor (i.e., :ada:`Model = Sensor_V2`), a new component (:ada:`Info_2`) is available. -The :ada:`Read_Info` and :ada:`Display` subprograms have been adapted to -take this new model into account. In the :ada:`Read_Info` function, we return +The :ada:`Current_Info` and :ada:`Display` subprograms have been adapted to +take this new model into account. In the :ada:`Current_Info` function, we return information for the newer model of the sensor. In the :ada:`Display` procedure, we display the additional information provided by the newer model. @@ -3579,7 +3579,7 @@ Note that the original test application that makes use of the sensor procedure Show_Sensors is S1 : Sensor; begin - Display (Read_Info (S1)); + Display (Current_Info (S1)); end Show_Sensors; Because we have a default value for the discriminant of the :ada:`Sensor` type, @@ -3601,8 +3601,8 @@ sensor: S1 : Sensor; S2 : Sensor (Sensor_V2); begin - Display (Read_Info (S1)); - Display (Read_Info (S2)); + Display (Current_Info (S1)); + Display (Current_Info (S2)); end Show_Sensors; In the updated version of the :ada:`Show_Sensors` procedure, we're now using