From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,ad4aec717fd8556e X-Google-Attributes: gid103376,public From: mheaney@ni.net (Matthew Heaney) Subject: Re: 'size attribute inheritance Date: 1997/08/10 Message-ID: X-Deja-AN: 263419599 References: <33ECF679.4B5D@lmco.com> Organization: Estormza Software Newsgroups: comp.lang.ada Date: 1997-08-10T00:00:00+00:00 List-Id: In article <33ECF679.4B5D@lmco.com>, cpalenzuela@sprintmail.com wrote: >Following is a simplified version of our example. We are using Green >Hills Ada 95 targeted to Sun/Solaris. > > type Integer_16 is range -(2**15) .. (2**15)-1; > for Integer_16'size use 16; As Bob pointed out, you don't need the size clause, as that is the size already. The "default" size is the number of bits needed to represent all values in the range, no more, and no less. > subtype Natural_16 is Integer_16 range 0 .. Integer_16'last; > > subtype Month_Type is Natural_16 range 0..11; > > type Date_Type is > record > Year : Natural_16; > Month : Month_Type; > end record; > > for Date_Type use > record > Year at 0 range 0 .. 15; > Month at 0 range 16 .. 31; > end record; I would put a size clause on the record, too, and an alignment clause: for Data_Type'Size use 32; for Data_Type'Alignment use 4; -- I think that's the syntax > ... > When running this code, we get a bus alignment error. We tracked >it > down to the fact that the compiler does NOT really allocate 16 bits > for Month with subtype Month_Type. We assumed that since >Integer_16 > had been set up with 'size attribute of 16, subtype Natural_16 >would > inherit that and so Month_Type would also inherit it. I might >expect > based on 13.3 (55) that Month_Type'size = 4 and then on 13.3 (43) >that > Month'size >= 4. Perhaps the confusion is that only the attribute > itself and not its specific value is what is inherited; I don't see > anything in the ARM which says one way or the other. (Sub)type Month_Type really is 4 bits, as that's all that is required to represent all the values in the range 0 .. 11. It is definately not 16 bits, even though it's a subtype of a subtype that does have a size of 16. But you explicitly allocated 16 bits for the Month component of the Date_Type record, so that's what you got for that component. But read on. Given that you're using representation clauses on a type, I assume your reason for doing so is because you're going to read or write an object of this type across an external interface. As Bob also pointed out, YOU SHOULD NEVER USE CONSTRAINED SUBTYPES TO TRANSMIT OR RECIEVE DATA ACROSS AN EXTERNAL INTERFACE. I'll say this another way: USE ONLY FIRST NAMED SUBTYPES WHOSE RANGE EXACTLY MATCHES THE RANGE REPRESENTABLE BY ITS SIZE, WHEN PERFORMING EXTERNAL I/O. Some more advice USE THE TYPES IN PACKAGE INTERFACES TO PERFORM EXTERNAL I/O. I'm not trying to scream at anyone or be rude or anything, but this issue is so important that I have to get your attention, because most Ada programmers don't completely understand it. The correct way to declare your date type is as follows: type Date_Type is record Year : Interfaces.Integer_16; Month : Interfaces.Integer_16; end record; for Date_Type use record Year at 0 range 0 .. 15; Month at 0 range 16 .. 31; end record; for Date_Type'Size use 32; for Date_Type'Alignment use 4; When declaring actual objects of the type, for use when executing the I/O statement, then specify a size for the object: declare The_Date : Date_Type; for The_Date'Size use 32; begin read (The_Date'Address, nbytes => 4); end; I'm not sure, but it might help too to declare the object as aliased and volatile: declare The_Date : aliased Date_Type; for The_Date'Size use 32; pragma Volatile (The_Date); begin read (The_Date'Address, nbytes => 4); end; > Putting a 'size attribute on the subtype resulted in a syntax error > [assumedly based on 13.3 (48)]. It's not necessarily so bad that > Month_type'size and Month'size are different from Integer_16'size, >but > I would at least expect the values of Month to be right-justified >in > the record-component field if Month'size < Integer_16'size (as >here), > but instead it is left-justified! Is there a justification in the >ARM > for this or is this a compiler issue? You can only specify a size clause in the declaration of the first subtype (the declaration "type T is..."). I'm not sure what you meant by values being "left justified" or "right justified." Make sure you're not having any problems with endianness, either. On a big-endian machine, your declaration is layed out as 0 15 16 31 The_Date : year month on a little endian machine: 31 16 15 0 month year : The_Date If the value for month is at at wrong end of the month component, perhaps that's becuase you and the device have a different endianness? (The UNIX guys call this "getting NUXIed.") >We worked around this >problem > by setting up Month_Type as a type, rather than a subtype, and > assigning it a 'size of 16, but the issue still has me perplexed. >I > would appreciate any informed opinions as to what I can or should > expect about the inheritance of the 'size attribute. Technically, you meant to say "setting up Month_Type as a first named subtype." And no, size is not "inherited." The size is always only what is required to exactly represent all values of the subtype. For example, type Byte is range 0 .. 255; for Byte'Size use 8; subtype Nibble is Byte range 0 .. 15; Byte'Size is 8, and Nibble'Size is 4. Which is the reason you should always specify a size for objects (not just the type) used for external I/O (ie whenever you say "O'Address", you should already have said "for O'Size use ..."). This makes sense, if you consider this declare O : Nibble; begin A compiler can only allocate an integral number of "storage elements" for O. On a machine with byte addressing, O would "probably" have a size of 8, although it could have 16 or 32. There's no way object O could have a size of 4, in spite of the fact that Nibble has a size of 4. You the programmer must specify explictly what the size of the object needs to be, irrespective of whether the type has a size clause. As I said (loudly, but not to be rude), never ever do I/O using other than a first named subtype whose range exactly matches the values representable in its size. Types with this very characteristic are already defined for you in package Interfaces. If you did use a subtype with a range constraint to do external I/O, and you got garbage over the interface, now your program is erroneous (or maybe it's a "bounded error" now - check the AARM). For example, if I had a Date_Type object, and the device put the value 16#0000_00FF# at that address, then Date.Month has the value 255, even though it is constrained to only have the values 0 .. 11. If you ever try to deference that component, then all bets are off as to the program's behavior. (Think of the disaster that could happen if Month were in a case statement, or used to index an array.) Many, many new and experienced Ada programmers naively think that Constraint_Error will get raised. This is completely and totally incorrect. Program behavior is UNDEFINED. Perhaps your "bus alignment" error is really the result of erroneous execution, because you have garbage in the month component of date. You the programmer must manually check the object to make sure its value is in the correct range. So here's what you can do for the date: subtype Month_Type is Natural range 0 .. 11; declare The_Date : Date_Type; -- the declaration I wrote above for The_Date'Size use 32; begin read (The_Date'Address, nbytes => 4); if The_Date.Month not in 0 .. 11 then end if; ... end; Another possibility is that there was already garbage in the object when declared (because it wasn't given a default value), and the device wrote fewer bytes than the number of bytes in the object. For example declare Month : Month_Type; -- 16 bit object begin end; Now if there were garbage in the most significant byte of Month, Month would be outside its correct range, even though the device returned a correct value. Here's yet another reason to not use constrained subtypes. If I tried to check to make sure the object had a value in the correct range: if Month not in Month_Type'Range then end if; the compiler might optimize the check away, under the assumption that since Month is of subtype Month_Type, it can't be outside that subtype's range, and therefore we can just get rid of this unnecessary check. (There's a T'Value attribute too, but I'm still not sure a use of it won't get optimized away.) Which is why I always read into unconstrained subtypes, and do the constraint checking manually. Hope that helps, Matt -------------------------------------------------------------------- Matthew Heaney Software Development Consultant (818) 985-1271