From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.5-pre1 (2020-06-20) on ip-172-31-74-118.ec2.internal X-Spam-Level: X-Spam-Status: No, score=-1.9 required=3.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.5-pre1 Date: 2 Sep 93 15:43:57 GMT From: dog.ee.lbl.gov!overload.lbl.gov!agate!howland.reston.ans.net!noc.near.net !inmet!spock!stt@ucbvax.Berkeley.EDU (Tucker Taft) Subject: Re: Unchecked_Conversion question Message-ID: List-Id: In article <1993Sep2.133859.26958@Rapnet.Sanders.Lockheed.Com> lvonrude@Rapnet.Sanders.Lockheed.Com (Lowell S. VonRuden x5294) writes: >In article > adam@irvine.com (Adam Beneschan) writes: >>In article <1993Sep1.154715.10498@Rapnet.Sanders.Lockheed.Com> >> lvonrude@Rapnet.Sanders.Lockheed.Com (Lowell S. VonRuden x5294) writes: >>> I am doing something that seems to work using a Verdix compiler, but I >>> have not been able to determine if this is something that will be safely >>> transportable. Hopefully, someone here can tell me. >>> >>> I have a 32 bit value coming in from an external interface as an >>> integer, which I am mapping to an enumeration type. The enumeration >>> type has representation clauses for both size (Integer'Size) and >>> implementation values. Assigning the result of an unchecked conversion >>> from the integer to an object of the enumeration type doesn't raise any >>> exception if the integer is out of range for the enumeration type >>> representation. I found that if I do an explicit conversion of the >>> enumeration object to its type, then the range gets checked. In my view, it is generally unwise to rely on type conversions (checked or unchecked) to perform data validation. It is easy enough (if a bit tedious) to perform data validation yourself. Because this has been a common problem, we have proposed a standard object attribute in Ada 9X called "Valid" which checks whether the current contents of an object corresponds to the representation for a valid value of its subtype. Hence, after "E := Unchecked_Convert(Int);" one will be able to write: if not E'Valid then else end if; (Note that the 'Valid attribute is only proposed for scalar types, since checking "validity" for access types and composite types was felt to be too difficult to define in any semantically formal way.) >>> procedure Sample (Int : Integer) is >>> >>> type Enum is (AAA, BBB, CCC, DDD); >>> for Enum use (AAA=> 1, >>> BBB=> 2, >>> CCC=> 13, >>> DDD=> 14); >>> for Enum'Size use Integer'Size; >>> >>> function Convert is new Unchecked_Conversion (Source => Integer, >>> Target => Enum); >>> begin >>> >>> E := Convert (Int); -- no exception raised here if Int is out of range >>> >>> E := Enum (Convert (Int)); -- this does raise constraint error if >>> -- Int is out of range >>> >>> end Sample; >>> >>> >>> So, is this a dependable thing to do? >> >>I don't think so.... I agree with this assessment >That's what I'm concerned about. The Verdix compiler I'm using >generates the check for the seemingly unnecessary conversion both with >and without the optimizer turned on, but I still feel funny trusting >this in all situations. > >I saw someone else's attempt at trying to catch bad values for X. They >passed the Enum resulting from the unchecked conversion into a procedure, >which did some unrelated thing, but they had a local block with an exception >handler for Constraint_Error surrounding the call. This would assume >that the constraints of the Enum type would always be checked when the >object is passed to another procedure. > > E := Convert (Int); > > begin > Do_Something_Unrelated (E); > exception > when Constraint_Error => > -- assume Int must not have been a valid Enum representation > end; > >Would this be a safe assumption? If you are going to all this trouble, you might be better off checking the validity of the value before you convert it from an integer type. There are various ways to do this, depending on your time/space tradeoffs. You might want to start by defining the enumeration representation using named numbers, e.g: type Enum is (AAA, BBB, CCC, DDD); AAA_Rep : constant := 1; BBB_Rep : constant := 2; CCC_Rep : constant := 13; DDD_Rep : constant := 14; for Enum use (AAA=>AAA_Rep, BBB=>BBB_Rep, CCC=>CCC_Rep, DDD=>DDD_Rep); Enum_Min_Rep : constant := AAA_Rep; Enum_Max_Rep : constant := DDD_Rep; Given the above, you can now pretty easily write whatever kind of validation routine you want. For example, you could create a boolean array: type Validity_Array is array(Enum_Min_Rep .. Enum_Max_Rep) of Boolean; Is_Valid : constant Validity_Array := Validity_Array'(AAA_Rep|BBB_Rep|CCC_Rep|DDD_Rep => True, others => False); function Is_Valid_Rep(Rep : Integer) return Boolean; pragma Inline(Is_Valid_Rep); function Is_Valid_Rep(Rep : Integer) return Boolean is begin return Rep in Is_Valid'Range and then Is_Valid(Rep); end Is_Valid_Rep; My general rule would be to do things the obvious, explicit way, rather than try to use features of the language in a way that is not guaranteed to work, with a hope that the compiler will figure out what you mean, and "do the right thing." Of course, as indicated above, the obvious, explicit way is pretty tedious if you have to do this for every enumeration type, which is one reason why we have proposed the 'Valid attribute. But if you only have one or two such enumeration types in you program, you would be wise, at least until "'Valid" shows up in your local Ada compiler, to use an explicit, if tedious, validity test before any conversions. P.S. My own view of enumeration representation clauses is that they should be used only when really necessary. The problem is that the attributes 'Pos, 'Val, 'Succ, 'Pred, 'Image, and 'Value, for-loops over the enumeration type, array indexing with the enumeration type, etc. have to do some hidden (and potentially inefficient) stuff to work with "holey" enumeration types. Anytime there is a simple-looking feature with hidden implementation overhead, one should be careful in use of the feature. In most applications I have seen, the only reason for a "holey" enumeration type was that there was just no name for some of the intermediate values; there was generally no requirement to be able to iterate over the named enumeration values, automatically skipping the unnamed values (the automatic "skipping" is what makes for-loops a pain). For example, one might be tempted to define EBCDIC as a "holey" enumeration type, since some of the 8-bit codes have no corresponding graphic character. However, this would be a big mistake (the reason is left as an exercise for the reader). Given the above, it is often more straightforward to either specify "fillers" for the gaps in the enumeration type (that's essentially what is done for the predefined enumeration type Character), or stick with an integer type (perhaps visibly "private") while providing named constants corresponding to the interesting values. Which approach is best depends on how sparse is the type. Any of these approaches makes many things more straightforward, including data validity tests, and eliminates the hidden baggage from 'Pos, 'Val, for-loops, array indexing, etc. For example, in your case, the type is sparse enough that an integer type with named constants might be best: type Enum is range 1..14; AAA : constant Enum := 1; BBB : constant Enum := 2 CCC : constant Enum := 13; DDD : constant Enum := 14; Note that because Ada has strong type checking for user-defined integer types (unlike C or C++), there are no implicit conversions from other named integer types to Enum, so you get some amount of protection. If you want to enforce further privacy, then you could put the above in a private part, and make only the following visible: type Enum is private; AAA : constant Enum; BBB : constant Enum; CCC : constant Enum; DDD : constant Enum; function Convert(Rep : Integer) return Enum; -- raises Invalid_Rep if Integer not a valid value Invalid_Rep : exception; private type Enum is range 1..14; ...etc... See above So the bottom line might be, you should consider an alternative to using "holey" enumeration types. >-- Usual disclaimers apply... Lowell Von Ruden -- >-- lvonrude@rapnet.sanders.lockheed.com Lockheed Sanders, Inc -- S. Tucker Taft stt@inmet.com Intermetrics, Inc. Cambridge, MA 02138