@@ -1071,11 +1071,15 @@ component :ada:`Arr`. Note that the same discriminant part must appear in both
10711071:ref: `the partial and the full view <Adv_Ada_Type_View >` of type :ada: `T `.
10721072
10731073
1074+ .. _Adv_Ada_Record_Discriminants_Object_Declaration :
1075+
10741076Object declaration
10751077~~~~~~~~~~~~~~~~~~
10761078
10771079As we've already seen, we declare objects of a type :ada: `T ` with a
10781080discriminant :ada: `D ` by specifying the actual value of discriminant :ada: `D `.
1081+ This is called a
1082+ :ref: `discriminant constraint <Adv_Ada_Record_Discriminant_Constraints >`.
10791083For example:
10801084
10811085.. code :: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants.Objects_Discriminants
@@ -2108,20 +2112,307 @@ value set for the parent type :ada:`T`.
21082112 Instead of using :ada: `<> `, we have to repeat the value explicitly.
21092113
21102114
2111- ..
2112- TO BE DONE:
2115+ .. _Adv_Ada_Record_Discriminant_Constraints_Operations :
21132116
2114- Discriminant constraints and operations
2115- ---------------------------------------
2117+ Discriminant constraints and operations
2118+ ---------------------------------------
21162119
2117- .. admonition :: In the Ada Reference Manual
2120+ In this section, we discuss some details about discriminant constraints and
2121+ operations related to discriminants |mdash | more specifically, the
2122+ :ada: `Constrained ` attribute.
21182123
2119- - :arm: ` 3.7.1 Discriminant Constraints <3-7-1> `
2124+ .. admonition :: In the Ada Reference Manual
21202125
2121- .. todo ::
2126+ - :arm: `3.7.1 Discriminant Constraints <3-7-1> `
2127+
2128+
2129+ .. _Adv_Ada_Record_Discriminant_Constraints :
2130+
2131+ Discriminant constraints
2132+ ~~~~~~~~~~~~~~~~~~~~~~~~
2133+
2134+ As we discussed before, when
2135+ :ref: `declaring an object with a discriminant <Adv_Ada_Record_Discriminants_Object_Declaration >`,
2136+ we have to specify the values of the all discriminants |mdash | unless, of
2137+ course, those discriminants have a
2138+ :ref: `default value <Adv_Ada_Record_Discriminants_Default_Values >`. The values
2139+ we specify for the discriminants are called discriminant constraints.
2140+
2141+ Let's revisit the code example we've seen earlier on:
2142+
2143+ .. code :: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Discriminant_Constraint
2144+
2145+ package Recs is
2146+
2147+ type T (L : Positive;
2148+ M : Positive) is
2149+ null record;
2150+
2151+ end Recs;
2152+
2153+ with Recs; use Recs;
2154+
2155+ procedure Show_Object_Declaration is
2156+ A : T (L => 5, M => 6);
2157+ B : T (7, 8);
2158+ C : T (7, M => 8);
2159+ begin
2160+ null;
2161+ end Show_Object_Declaration;
2162+
2163+ Here, :ada: `L => 5, M => 6 ` (for object :ada: `A `) are named constraints, while
2164+ :ada: `7, 8 ` (for object :ada: `B `) are positional constraints.
2165+
2166+ It's possible to use both positional and named constraints, as we do for object
2167+ :ada: `C `: :ada: `7, M => 8 `. In this case, the positional associations must
2168+ precede the named associations.
2169+
2170+ In the case of named constraints, we can use multiple selector names:
2171+
2172+ .. code :: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Discriminant_Constraint
2173+
2174+ with Recs; use Recs;
2175+
2176+ procedure Show_Object_Declaration is
2177+ A : T (L | M => 5);
2178+ -- ^^^^^
2179+ -- multiple selector names
2180+ begin
2181+ null;
2182+ end Show_Object_Declaration;
2183+
2184+ This is only possible, however, if those named discriminants are all of the
2185+ same type. (In this case, :ada: `L ` and :ada: `M ` are both of :ada: `Positive `
2186+ subtype.)
2187+
2188+ .. admonition :: In the Ada Reference Manual
2189+
2190+ - :arm22: `3.7.1 Discriminant Constraints <3-7-1> `
2191+
2192+
2193+ Discriminant constraint in subtypes
2194+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2195+
2196+ We can use discriminant constraints in the declaration of subtypes. For
2197+ example:
2198+
2199+ .. code :: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Discriminant_Constraint
2200+
2201+ with Recs; use Recs;
2202+
2203+ procedure Show_Object_Declaration is
2204+ subtype T_5_6 is T (L => 5, M => 6);
2205+ -- ^^^^^^^^^^^^^^
2206+ -- discriminant constraints for subtype
2207+
2208+ A : T_5_6;
2209+ begin
2210+ null;
2211+ end Show_Object_Declaration;
2212+
2213+ In this example, we use the named discriminant constraints
2214+ :ada: `L => 5, M => 6 ` in the declaration of the subtype :ada: `T_5_6 `.
2215+
2216+
2217+ .. _Adv_Ada_Constrained_Attribute :
2218+
2219+ Constrained Attribute
2220+ ~~~~~~~~~~~~~~~~~~~~~
2221+
2222+ We can use the :ada: `Constrained ` attribute to verify whether an object of
2223+ discriminated type is constrained or not. Let's look at a simple example:
2224+
2225+ .. code :: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Simple_Constrained_Attribute
2226+
2227+ package Recs is
2228+
2229+ type T (L : Positive := 1) is
2230+ null record;
2231+
2232+ end Recs;
2233+
2234+ with Ada.Text_IO; use Ada.Text_IO;
2235+
2236+ with Recs; use Recs;
2237+
2238+ procedure Show_Constrained_Attribute is
2239+ Constr : T (L => 5);
2240+ -- ^^^^^^ constrained.
2241+ Unconstr : T;
2242+ -- ^ unconstrained;
2243+ -- using defaults.
2244+ begin
2245+ Put_Line ("Constr'Constrained: "
2246+ & Constr'Constrained'Image);
2247+ Put_Line ("Unconstr'Constrained: "
2248+ & Unconstr'Constrained'Image);
2249+ end Show_Constrained_Attribute;
2250+
2251+ As the :ada: `Constrained ` attribute indicates, the :ada: `Constr ` object is
2252+ constrained (by the :ada: `L => 5 ` discriminant constraint), while the
2253+ :ada: `Unconstr ` object is unconstrained. Note that, even though :ada: `Unconstr `
2254+ is using the default value for :ada: `L ` |mdash | which would correspond to the
2255+ discriminant constraint :ada: `L => 1 ` |mdash | the object itself hasn't been
2256+ constraint at its declaration.
2257+
2258+ Let's continue our discussion with a more complex example by reusing
2259+ the :ada: `Unconstrained_Types ` package that we declared in a
2260+ :ref: `previous section <Adv_Ada_Definite_Indefinite_Subtypes >`. In this
2261+ version of the package, we're adding a :ada: `Reset ` procedure for the
2262+ discriminated record type :ada: `Simple_Record `:
2263+
2264+ .. code :: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Constrained_Attribute
2265+
2266+ package Unconstrained_Types is
2267+
2268+ type Simple_Record
2269+ (Extended : Boolean := False) is
2270+ record
2271+ V : Integer;
2272+ case Extended is
2273+ when False =>
2274+ null;
2275+ when True =>
2276+ V_Float : Float;
2277+ end case;
2278+ end record;
2279+
2280+ procedure Reset (R : in out Simple_Record);
2281+
2282+ end Unconstrained_Types;
2283+
2284+ with Ada.Text_IO; use Ada.Text_IO;
2285+
2286+ package body Unconstrained_Types is
2287+
2288+ procedure Reset (R : in out Simple_Record) is
2289+ Zero_Not_Extended : constant
2290+ Simple_Record := (Extended => False,
2291+ V => 0);
2292+
2293+ Zero_Extended : constant
2294+ Simple_Record := (Extended => True,
2295+ V => 0,
2296+ V_Float => 0.0);
2297+ begin
2298+ Put_Line ("---- Reset: R'Constrained => "
2299+ & R'Constrained'Image);
2300+
2301+ if not R'Constrained then
2302+ R := Zero_Extended;
2303+ else
2304+ if R.Extended then
2305+ R := Zero_Extended;
2306+ else
2307+ R := Zero_Not_Extended;
2308+ end if;
2309+ end if;
2310+ end Reset;
2311+
2312+ end Unconstrained_Types;
2313+
2314+ As the name indicates, the :ada: `Reset ` procedure initializes all record
2315+ components with zero. Note that we use the :ada: `Constrained ` attribute to
2316+ verify whether objects are constrained before assigning to them. For objects
2317+ that are not constrained, we can simply assign another object to it |mdash | as
2318+ we do with the :ada: `R := Zero_Extended ` statement. When an object is
2319+ constrained, however, the discriminants must match. If we assign an object to
2320+ :ada: `R `, the discriminant of that object must match the discriminant of
2321+ :ada: `R `. This is the kind of verification that we do in the :ada: `else ` part
2322+ of that procedure: we check the state of the :ada: `Extended ` discriminant
2323+ before assigning an object to the :ada: `R ` parameter.
2324+
2325+ The :ada: `Using_Constrained_Attribute ` procedure below declares two objects of
2326+ :ada: `Simple_Record ` type: :ada: `R1 ` and :ada: `R2 `. Because the
2327+ :ada: `Simple_Record ` type has a default value for its discriminant, we can
2328+ declare objects of this type without specifying a value for the discriminant.
2329+ This is exactly what we do in the declaration of :ada: `R1 `. Here, we don't
2330+ specify any constraints, so that it takes the default value
2331+ (:ada: `Extended => False `). In the declaration of :ada: `R2 `, however, we
2332+ explicitly set :ada: `Extended ` to :ada: `False `:
2333+
2334+ .. code :: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Constrained_Attribute
2335+
2336+ with Ada.Text_IO; use Ada.Text_IO;
2337+
2338+ with Unconstrained_Types; use Unconstrained_Types;
2339+
2340+ procedure Using_Constrained_Attribute is
2341+ R1 : Simple_Record;
2342+ R2 : Simple_Record (Extended => False);
2343+
2344+ procedure Show_Rs is
2345+ begin
2346+ Put_Line ("R1'Constrained => "
2347+ & R1'Constrained'Image);
2348+ Put_Line ("R1.Extended => "
2349+ & R1.Extended'Image);
2350+ Put_Line ("--");
2351+ Put_Line ("R2'Constrained => "
2352+ & R2'Constrained'Image);
2353+ Put_Line ("R2.Extended => "
2354+ & R2.Extended'Image);
2355+ Put_Line ("----------------");
2356+ end Show_Rs;
2357+ begin
2358+ Show_Rs;
2359+
2360+ Reset (R1);
2361+ Reset (R2);
2362+ Put_Line ("----------------");
2363+
2364+ Show_Rs;
2365+ end Using_Constrained_Attribute;
2366+
2367+ When we run this code, the user messages from :ada: `Show_Rs ` indicate to us
2368+ that :ada: `R1 ` is not constrained, while :ada: `R2 ` is constrained.
2369+ Because we declare :ada: `R1 ` without specifying a value for the :ada: `Extended `
2370+ discriminant, :ada: `R1 ` is not constrained. In the declaration of
2371+ :ada: `R2 `, on the other hand, the explicit value for the :ada: `Extended `
2372+ discriminant makes this object constrained. Note that, for both :ada: `R1 ` and
2373+ :ada: `R2 `, the value of :ada: `Extended ` is :ada: `False ` in the declarations.
2374+
2375+ As we were just discussing, the :ada: `Reset ` procedure includes checks to avoid
2376+ mismatches in discriminants. When we don't have those checks, we might get
2377+ exceptions at runtime. We can force this situation by replacing the
2378+ implementation of the :ada: `Reset ` procedure with the following lines:
2379+
2380+ .. code-block :: ada
2381+
2382+ -- [...]
2383+ begin
2384+ Put_Line ("---- Reset: R'Constrained => "
2385+ & R'Constrained'Image);
2386+ R := Zero_Extended;
2387+ end Reset;
2388+
2389+ Running the code now generates a runtime exception:
2390+
2391+ ::
2392+
2393+ raised CONSTRAINT_ERROR : unconstrained_types.adb:12 discriminant check failed
2394+
2395+ This exception is raised during the call to :ada: `Reset (R2) `. As we see in the
2396+ code, :ada: `R2 ` is constrained. Also, its :ada: `Extended ` discriminant is set
2397+ to :ada: `False `, which means that it doesn't have the :ada: `V_Float `
2398+ component. Therefore, :ada: `R2 ` is not compatible with the constant
2399+ :ada: `Zero_Extended ` object, so we cannot assign :ada: `Zero_Extended ` to
2400+ :ada: `R2 `. Also, because :ada: `R2 ` is constrained, its :ada: `Extended `
2401+ discriminant cannot be modified.
2402+
2403+ The behavior is different for the call to :ada: `Reset (R1) `, which works fine.
2404+ Here, when we pass :ada: `R1 ` as an argument to the :ada: `Reset ` procedure, its
2405+ :ada: `Extended ` discriminant is :ada: `False ` by default. Thus, :ada: `R1 ` is
2406+ also not compatible with the :ada: `Zero_Extended ` object. However, because
2407+ :ada: `R1 ` is not constrained, the assignment modifies :ada: `R1 ` (by changing
2408+ the value of the :ada: `Extended ` discriminant). Therefore, with the call to
2409+ :ada: `Reset `, the :ada: `Extended ` discriminant of :ada: `R1 ` changes from
2410+ :ada: `False ` to :ada: `True `.
2411+
2412+ .. admonition :: In the Ada Reference Manual
2413+
2414+ - :arm22: `3.7.2 Operations of Discriminated Types <3-7-2> `
21222415
2123- - Discriminant constraint
2124- - (MOVE: Constrained Attribute)
21252416
21262417
21272418..
0 commit comments