!standard 13.13.1 (00) 99-02-23 AI95-00195/03 !class binding interpretation 98-03-27 !status received 98-03-27 !reference AI95-00108 !reference AI95-00145 !priority High !difficulty Hard !subject Streams !summary 99-02-23 1 - When the predefined Input attribute creates an object, this object undergoes default initialization and finalization. 2 - In determining whether a stream-oriented attribute has been specified for a limited type, the normal visibility rules are applied to the attribute_definition_clause. 3 - For a derived type which is limited (tagged or not), the attributes Read and Write are inherited, and the attributes Input and Output revert to their predefined definition (i.e. they cannot be called). (This amends AI95- 00108.) 4 - In the profiles of the stream-oriented attributes, the notation "italicized T" refers to the base type for a scalar type, and to the first subtype otherwise. 5 - For an untagged derived type with new discriminants that have defaults, the predefined stream-oriented attributes read or write the new discriminants, not the old ones. (This amends AI95-00108.) 6 - The predefined Read attribute for composite types with defaulted discriminants must ensure that, if exceptions are raised by the Read attribute for some discriminant, the discriminants of the actual object passed to Read are not modified. This may require the creation of an anonymous object, which undergoes initialization and finalization. 7 -The predefined Read attribute for composite types with defaulted discriminants must raise Constraint_Error if the discriminants found in the stream differ from those of the actual parameter to Read, and this parameter is constrained. 8 - If S is a subtype of an abstract type, an attribute_reference for S'Input is illegal unless this attribute has been specified by an attribute_definition_clause. 9 - The number of calls performed by the predefined implementation of the stream-oriented attributes to the Read and Write operations of the stream type is unspecified. However, all the calls to Read and Write needed to implement a top-level invocation of a stream-oriented attribute must take place before this top-level invocation returns. 10 - The predefined stream-oriented attributes for a scalar type shall only read or write the minimum number of stream elements required by the first subtype of the type. Constraint_Error is raised if such an attribute is passed (or would return) a value outside the range of the first subtype. 11 - In an attribute_definition_clause for a stream-oriented attribute, the name shall not denote an abstract subprogram. !question 98-04-04 1 - RM95 13.13.2(27) states that S'Input "creates an object (with the bounds or discriminants, if any, taken from the stream), initializes it with S'Read, and returns the value of the object." Does the verb "initialize" in this sentence refer to the entire initialization process mentioned in RM95 3.3.1(18) and 7.6(10)? (yes) In particular, if S is a controlled subtype, or if it contains controlled components, is the Initialize subprogram called? (yes) Is the Finalize subprogram called when the intermediate object is finalized? (yes) For a record type whose components have initial values, are these values evaluated? (yes) 2 - RM95 13.13.2(36) states that "an attribute_reference for one of these attributes is illegal if the type is limited, unless the attribute has been specified by an attribute definition clause." If some stream-oriented attribute has been specified in a private part, and we are at a point that doesn't have visibility over that private part, is a reference to the attribute legal? (no) 3 - Let T be a limited type with an attribute_definition_clause for attribute Read, and D a type derived from T, and assume that there is no attribute_definition_clause for D'Read. Is a usage of D'Read legal? (yes) 4 - The definition of the profiles of S'Read, S'Write, S'Input and S'Output given in RM95 13.13.2 uses the notation "italicized T" for the type of the Item parameter. What is the meaning of "italicized T" in this context? (T'Base for scalars, first subtype otherwise.) 5 - AI95-00108 states (in the !discussion section) that "for untagged derived types, there is no problem for the derived type inheriting the stream attributes." This doesn't seem clear if the derived type includes a known discriminant part. Consider: type Parent (D1, D2 : Integer := 1) is ...; type Child (D : Integer := 2) is new Parent (D1 => D, D2 => D); Clearly Parent'Write writes two discriminant values. How many discriminants does Child'Write write? (one) 6 - RM95 13.13.2(9) states that for a record type, the predefined S'Read reads the components in positional aggregate order. However, the language doesn't seem to specify what happens when exceptions are raised by the calls to the Read attribute for the components. Consider for example the following type declarations: type T1 is range ...; type T2 is range ...; type R (D1 : T1 := ...; D2 : T2 := ...) is record ... end record; Say that attributes_definition_clauses have been given for T1'Read and T2'Read, and consider a call to R'Read. Assume that, during this call, an exception is raised by T2'Read. Is the discriminant X.D1 modified? (no) 7 - Consider a call to T'Read where T is a type with defaulted discriminants. If the discriminants found in the stream have values different from those of the discriminants of the object passed to T'Read for the Item parameter, and that object is constrained, is Constraint_Error raised? (yes) 8 - If T is an abstract type, is the function T'Input abstract? (no, but it cannot be called) 9 - RM95 13.13.1(1) states that "T'Read and T'Write make dispatching calls on the Read and Write procedures of the type Root_Stream_Type." Is the number of those calls specified? (no) 10 - For a scalar type T, the second parameter of the predefined stream attributes is of type T'Base. Given the same type declaration for T, different compilers may choose different base ranges, and therefore write different numbers of storage units to the stream. This compromises portability, and makes it difficult to use streams to match a file format defined externally to Ada. 11 - In an attribute_definition_clause for a stream attribute, is it legal to give a name that denotes an abstract subprogram? (no) !recommendation 98-04-04 See summary. !wording 98-04-04 See summary. !discussion 99-02-23 1 - It seems logical to make Input follow as closely as possible the semantics of code that could be written by a user, so the implementation of S'Input should obtain the discriminants or bounds from the stream, and then declare an object with appropriate discriminants or bounds, as in: declare Anon : S (); begin S'Read (..., Anon); end; Accordingly, the initialization described in RM95 3.3.1(8-9) and RM95 7.6(10) takes place when Anon is declared, and the finalization described in RM95 7.6.1 takes place when Anon disappears (and before returning from the call to S'Input). Note that as part of initialization, compiler-specific fields are initialized as required by the implementation (and as permitted by AARM 3.3.1(14.a)). 2 - Consider for example: package P is type T is limited private; private procedure Read (Stream : ...; Item : out T); for T'Read use Read; end P; with P; procedure Q is X : P.T; begin P.T'Read (..., X); -- Illegal? end Q; The call to P.T'Read is illegal, because Q doesn't have visibility over the private part of P, which contains the attribute_definition_clause for attribute Read. On the other hand, at a place that has visibility over the private part of P (and that comes after the attribute_definition_clause) a reference to T'Read is legal. This rule is necessary to preserve the privacy of private types. Note that it is the location of the attribute_definition_clause that counts, not that of the subprogram specified in that clause. Thus, if the procedure Read above were moved to the visible part of P, a reference to P.T'Read would still be illegal (but a reference to P.Read wouldn't). 3 - AI95-00108 states that "for a type extension, the predefined Read attribute is defined to call the Read of the parent type, followed by the Read of the non-inherited components, if any, in canonical order." This rule doesn't work for limited (tagged) types, because the non- inherited components might include protected objects or tasks for which the predefined Read and Write attributes cannot be called. For limited derived types (tagged or not), the only sensible rule is that Read and Write are inherited "as is". This is consistent with what happens with the operator "=". On the other hand the attributes Input and Output cannot be inherited, as explained in the discussion of AI95- 00108. Therefore, these attributes must revert to their predefined definition, which means that they cannot be called, as stated in RM95 13.13.2(36). 4 - AI95-00145 specifies the meaning of the notation "italicized T" for operators as follows: "The italicized T shown in the definitions of predefined operators means: - T'Base, for scalars - the first subtype, for tagged types - the type without any constraint, in other cases" In the case of stream-oriented attributes the notation "italicized T" must be consistent with the parameter subtype required for attribute_definition_clauses. If we chose the same rule as for operators, we would have a discrepancy in the case of constrained untagged types, and this would unnecessarily complicate the static and dynamic semantics. When one of the stream-oriented attributes is specified by an attribute definition clause, RM95 13.13.2(36) states that "the subtype of the Item parameter shall be the base subtype if scalar, and the first subtype otherwise." Therefore, in the parameter profiles for the stream-oriented attributes in section 13.13, the notation "italicized T" means: - T'Base, for scalars - the first subtype, in other cases 5 - The inheritance rule given in AI95-00108 should only apply to those attributes that have been specified for the parent type. If this rule was applied to the predefined stream-oriented attributes, it would require, in the example given, to read or write two discriminants, because the predefined Read and Write attributes of type Parent do read or write two discriminants. But that would be inconsistent with the rule given in RM95 13.13.2(9): "the Read or Write attribute for each component is called in canonical order," since D1 and D2 are not components of type Child. Furthermore, definiteness can be changed by type derivation, and the dynamic semantics of Read and Write depend on definiteness. Consider the following modification of the original example: type Parent (D1, D2 : Integer) is ...; type Child (D : Integer := 2) is new Parent (D1 => D, D2 => D); In this case the predefined stream-oriented attributes for type Parent do not read or write the discriminants, so applying the inheritance rule of AI95- 00108 would cause the stream-oriented attributes for Child to not read or write any discriminant, which doesn't make sense. Therefore, RM95 13.13.2(9) must have precedence, and the predefined stream- oriented attributes for Child only read or write exactly one discriminant, D. The underlying model is that the predefined stream-oriented attributes are created anew for each type declaration, based on the structure of the type, much like predefined operators. 6 - The problem mentioned in the question only exists for a type R that is passed by-reference to R'Read: obviously if the type is passed by copy, an exception raised by R'Read cannot affect the original object. In the case of a type which is passed by-reference, we must consider two cases: - If the exception in T2'Read is due to the failure of some language-defined check, RM95 11.6(6) explains that the object or its parts may become abnormal, so we don't have to specify what happens to the discriminants. - If the exception in T2'Read is due to an explicit raise, the object obviously doesn't become abnormal, and therefore we must preserve the integrity of its discriminants. In other words, either all discriminants are updated (if the calls to the Read attributes for the discriminants were successful) or none (if any call to a Read attribute for a discriminant raised an exception). This model requires an implementation to read the discriminants, create an anonymous object using the given discriminants and assign that object to the formal parameter Item. The normal initialization and finalization take place for this anonymous object. Strictly speaking, the use of an anonymous object is only required for a type with defaulted discriminants which is passed by-reference, if the actual parameter of Read is not constrained. However, an implementation is free to use anonymous objects in other cases. Use of an anonymous object is only required for discriminants. An implementation is free to read the other components directly in the Item parameter. For example, if we change the original example as follows: type R is record C1 : T1; C2 : T2; end record; then if T2'Read raises an exception when reading component C2, it is unspecified if C1 is modified or not. 7 - When the type has defaulted discriminants, the predefined Read attribute must read them from the stream. It can be the case that the actual object passed to Read is constrained. In this case, the discriminants found in the stream may or may not match those of the actual. If they don't match, Constraint_Error is raised, and this is a Discriminant_Check. It is unspecified whether this effect is achieved by assigning a temporary object, as explained in #6 above, or by other means. 8 - If T is an abstract type, calling the function T'Input would effectively create an object whose tag designates T, which is absurd. We could decide that T'Input is abstract, but it seems simpler to say that any attribute_reference for this attribute is illegal, by analogy with the rule stated in RM95 13.13.2(36) for limited types. 9 - Surely the user could count the calls to the Read and Write operations for the stream type by writing a pervert implementation of these operations, but it seems that performance should have precedence: for the predefined implementation of String'Write, requiring a call to Ada.Streams.Write for each character would be a big performance hit, with no real benefits. Therefore, the number of calls to the Read and Write operations is unspecified, and implementations are free (and in fact advised) to do internal buffering. However, we don't want to allow an implementation to buffer all stream output and do only one call to Write when the program terminates: the user must be able to assume reasonable properties regarding the underlying buffering mechanism. That's why we require that all the calls to Read or Write take place before the top-level call to a stream-oriented attribute completes. In other words, an implementation may combine several consecutive calls to Write into a single one, provided these calls all pertain to a single top-level call to the attribute Write (or Output). 10 - Consider the declaration: type T is range 1 .. 10; This declaration "defines an integer type whose base range includes at least the values [1 and 10] and is symmetric around zero, excepting possibly an extra negative value," as explained in RM95 3.5.4(9). Based on this rule (and assuming typical hardware), an implementation might choose an 8-, 16-, 32- or 64-bit base type for T. RM95 13.13.2(17) advise the implementation to "use the smaller number of stream elements needed to represent all values in the base range of the scalar type" when reading or writing values of type T. Clearly this is a portability issue: if two implementation use (as is typical) 8-bit stream elements, but have different rules for selecting base types, the number of elements read to or written from a stream will differ. This makes it very hard to write stream operations that comply with an externally defined format. In the above case, it would seem reasonable to read or write only the minimum number of stream elements necessary to represent the range 1 .. 10. This would remove the dependency on the base type selection, and make it easier to write portable stream operations. (There is still the possibility that different implementations would choose different sizes for stream elements, but that doesn't seem to happen in practice on typical hardware.) The only issue with that approach is that the stream-oriented attributes for scalar types have a second parameter of type T'Base, e.g.: procedure S'Write (Stream : access Ada.Streams.Root_Stream_Type'Class; Item : in T'Base); So one might call T'Write with the value 1000 for the Item parameter, and this might exceed the range representable in the stream. However, this usage is non-portable in the first place (because it depends on the choice of base range), so it doesn't seem important to preserve it. In fact any attempt at reading or writing a value outside the range of the first subtype is highly suspicious. Based on this reasoning, the following rules are added. Note that these rules are Dynamic Semantics rules, not Implementation Advices: - The predefined stream-oriented attributes for a scalar type T shall only read or write the minimum number of stream elements necessary to represent the first subtype. If S is the first subtype, the number of stream elements read to or written from the stream is exactly: (S'Size + Stream_Element'Size - 1) / Stream_Element'Size - If Write or Output is called with a value of the Item parameter outside the range of the first subtype, Constraint_Error is raised. This check is a Range_Check. - If the value extracted from the stream by Read or Input is outside the range of the first subtype, Constraint_Error is raised. This check is a Range_Check. 11 - Obviously it should not be possible to perform a non-dispatching call to an abstract subprogram (stream-oriented attributes are always called in a non- dispatching manner). Therefore, we have two options: - Make the attribute_definition_clause illegal. - Make the calls (explicit or implicit) illegal. The second option is a significant implementation burden, and allowing the attribute_definition_clause only to reject all calls doesn't seem to do any good. That's why the first option was preferred. !appendix 98-03-27 !section 13.13.1 !subject Various issues with the stream-oriented attributes !reference RM95 13.13.1 !reference RM95 13.13.2 !reference AI95-00108 !from Pascal Leroy 97-09-02 !reference 1997-15783.a Pascal Leroy 1997-9-2>> !discussion 1 - RM95 13.13.2(27) states that S'Input "creates an object (with the bounds or discriminants, if any, taken from the stream), initializes it with S'Read, and returns the value of the object. Does the verb "initialize" in this sentence refer to the entire initialization process mentioned in RM95 3.3.1(18) and 7.6(10)? In particular, if S is a controlled subtype, or if it contains controlled components, is the Initialize subprogram called? Is the Finalize subprogram called when the intermediate object is finalized? For a record type whose components have initial values, are these values evaluated? It seems that the simplest model is that S'Input declares a local object, which is then passed to S'Read, as in: X : S (); begin S'Read (..., X); If we don't do that, the object passed to S'Read might have uninitialized access values, or uninitialized compiler-specific fields (dopes, offsets, tag, etc.). 2 - RM95 13.13.2(36) states that "an attribute_reference for one of these attributes is illegal if the type is limited, unless the attribute has been specified by an attribute definition clause." If some stream-oriented attribute has been specified in a private part, and we are at a point that doesn't have visibility over that private part, is a reference to the attribute legal? For example: package P is type T is limited private; private procedure Read (Stream : ...; Item : out T); for T'Read use Read; end P; with P; procedure Q is X : T; begin T'Read (..., X); -- Legal? end Q; One hopes that the call to T'Read is illegal, overwise it would break the privacy of private types. 3 - Let T a limited type with an attribute_definition_clause for attribute Read, and D a type derived from T, and assume that there is no attribute definition clause for D'Read: type T is limited record ... end record; for T'Read use ...; type D is new T; Is a usage of D'Read legal or not? In other words, shall we consider that, for the purpose of checking RM95 13.13.2(36), "an attribute has been specified" for D? If the answer is "yes", how do the inheritance rules stated in AI95-00108 apply in the case where T is tagged, and D has a component of a task type in its record extension part? How is an implementation expected to read that component? 4 - The definition of the profiles of the predefined S'Read, S'Write, S'Input and S'Read given in RM95 13.13.2 uses the notation "T italic" for the type of the Item parameter. AI95-00145 explains that: "The italicized T shown in the definitions of predefined operators means: - T'Base, for scalars - the first subtype, for tagged types - the type without any constraint, in other cases" (Note that the above sentence says "operators", so strictly speaking it doesn't apply to attributes; it seems that the wording of this AI should be broadened.) When one of these attributes is specified by an attribute definition clause, RM95 13.13.2(36) states that "the subtype of the Item parameter shall be the base subtype if scalar, and the first subtype otherwise." There is a problem because these definitions don't coincide in the case of constrained array types. Consider: type T is new String (1 .. 10); It appears that the type of the Item parameter of the predefined stream-oriented attributes is "T without any constraint" (an anonymous unconstrained array type ), while the type of the Item parameter for a user-defined stream-oriented subprogram is T. In particular, does the presence or absence of an attribute definition clause have any bearing on the legality of the calls? For example, consider the legality of the 'others' choice in an array aggregate; it depends on the existence on an applicable index constraint, and therefore on whether the type of the parameter is constrained or not: package P is type T is new String (1 .. 10); procedure Write (...; Item : in T); private for T'Write use Write; end P; with P; procedure Q is begin T'Write (..., (others => 'a')); -- Legal? end; Is the above call legal? If yes, would it become illegal if the attribute_definition_clause were removed? A possible model is to say that an attribute_definition_clause changes the body of the Read attribute, but not its profile. It's as if the predefined Read was just a wrapper calling the user-specified subprogram. 5 - AI95-00108 states (in the !discussion section) that "for untagged derived types, there is no problem for the derived type inheriting the stream attributes." This doesn't seem clear if the derived type includes a known discriminant part. Consider: type Parent (D1, D2 : Integer := 1) is ...; type Child (D : Integer := 2) is new Parent (D1 => D, D2 => D); Clearly Parent'Write writes two discriminant values. It would seem that Child'Write should only write one discriminant value, which contradicts the simple inheritance rule given in the AI. 6 - RM95 13.13.2(9) states that for a record type, the predefined S'Read reads the components in positional aggregate order. However, the RM95 doesn't seem to specify what happens when exceptions are raised by the calls to the Read attribute for the components. Consider for example the following type declarations: type T1 is range ...; type T2 is range ...; type R (D1 : T1 := ...; D2 : T2 := ...) is record ... end record; Say that attributes_definition_clauses have been given for T1'Read and T2'Read, and consider the call: X : R; ... R'Read (..., X); Assume that an exception is raised by T2'Read. Is the discriminant X.D1 modified? That would be unpleasant if there were components depending on this discriminant! Consider a different example, where there are no discriminants: type T1 is ...; type T2 is ...; type R is record C1 : T1; C2 : T2; end record; Assume that an exception is raised by T2'Read. Is the component X.C1 modified? It would seem that we should stick to the notion that discriminants are only modified by an assignment of entire objects. This probably requires an intermediate object in the case of discriminated types. However, it would seem quite expensive to require the creation of such an intermediate object for types that don't have discriminants. Note that if we adopt the idea that an intermediate object is needed (at least in some cases) then we must define if initialization and finalization of this object take place. 7 - Consider a call to T'Read where T is a type with defaulted discriminants. If the discriminants found in the stream have values different from those of the discriminants of the object passed to T'Read for the Item parameter, and that object is constrained, is Constraint_Error raised? Note that if we adopt the model that an intermediate object is needed in this case, then the constraint check performed when assigning the intermediate object to the actual parameter will raise Constraint_Error. 8 - If T is an abstract type, is the function T'Input abstract? One hopes so, otherwise it makes it possible to create objects of an abstract type. 9 - RM95 13.13.1(1) states that "T'Read and T'Write make dispatching calls on the Read and Write procedures of the type Root_Stream_Type." Is the number of those calls specified? For example, for the predefined implementation of String'Write, is an implementation required to call Ada.Streams.Write for each character? or is it allowed to perform internal buffering and to call Ada.Streams.Write only once for the entire string? Obviously the user could tell the difference by writing a pervert implementation of the Write subprogram, but it would seem that performance considerations should have the priority in this case. _____________________________________________________________________ Pascal Leroy +33.1.30.12.09.68 pleroy@rational.com +33.1.30.12.09.66 FAX **************************************************************** From: Stephen Leake Sent: Tuesday, March 17, 1998 8:45 PM Subject: Ada Issue; 16 bit integers in streams !topic Stream size of 16 bit integers !reference 13.13.2 (17) !from Stephen Leake 98-03-14 !keywords base type, size clause, stream representation !reference as: 1998-15826.a Stephen Leake 1998-3-17 !discussion This is mainly a uniformity issue. There was a discussion of this issue on comp.lang.ada in September 1997, under the subject "16 bit integers in streams". The use of streams should be as portable as possible among compilers, at least when running on the same hardware. Currently, ObjectAda 7.1 represents a 16 bit integer by 32 bits in streams under Window 95/NT on Intel hardware, while GNAT represents a 16 bit integer by 16 bits in streams on the same system. Most C compilers on this system also use 16 bits in streams for a 16 bit integer. 13.13.2 (9) says the stream representation is implementation-defined, as it must be. However, the implementation advice 13.13.2 (17) says: If a stream element is the same size as a storage element, then the normal in-memory representation should be used by Read and Write for scalar objects. Otherwise, Read and Write should use the smallest number of stream elements needed to represent all values in the base range of the scalar type. Given a type defintion: type Int16 is range -32768 .. 32676; for Int16'size use 16; The "normal in-memory representation" is 16 bits. However, ObjectAda 7.1 uses 32 bits for this type in streams. This is because of RM95 13.13.2(36), which says in part: For an attribute_definition_clause specifying one of these attributes, the subtype of the Item parameter shall be the base subtype if scalar ... The rationale for this rule is that the user may declare a variable of the base type, and write it to a stream. ObjectAda chooses a 32 bit base integer in this case on Wintel systems; apparently for "efficient arithmetic". Ada has a general philosophy of giving the user as much control as possible over representation issues. However, the user has no control in this case (other than by choice of compiler). In principle, the user should be able to sacrifice efficient arithmetic for control over stream size. Clearly we cannot have arbitrary control over stream size; a 6 bit integer will never fit exactly in a stream element. But that is one reason to use a 16 bit integer in the first place - we know it can reasonably be represented in a stream. Proposed solutions: 1) Make a size clause influence the base type size. The base type size should be the minimum that can contain the first subtype, even if that is not the most "efficient". This gives the user control over the choice of speed or space. pragma Optimize (Space) could also influence this choice. It is not clear how to add this to the language in the reference manual; it would probably be just implementation advice. 2) Add a new attribute T'Stream_Size. The value must be a multiple of Stream_Element'size, and must be at least T'Size. It would be illegal to apply 'Read, 'Write, 'Input or 'Output to an object declared to be of type T'Base for a type T that has T'Stream_Size specified (or maybe just illegal to declare the variable of type T'Base in the first place). **************************************************************** From: Tucker Taft Sent: Tuesday, March 17, 1998 9:22 PM Subject: Re: Ada Issue; 16 bit integers in streams !topic Stream size of 16 bit integers !reference RM95 13.13.2 (17) !reference 1998-15826.a Stephen Leake 98-03-14 !from Tucker Taft 98-03-17 !keywords base type, size clause, stream representation !reference as: 1998-15827.a Tucker Taft 1998-3-17 !discussion : ... : Given a type defintion: : type Int16 is range -32768 .. 32676; : for Int16'size use 16; : The "normal in-memory representation" is 16 bits. However, ObjectAda : 7.1 uses 32 bits for this type in streams. This is because of RM95 : 13.13.2(36), which says in part: : For an attribute_definition_clause specifying one of these attributes, : the subtype of the Item parameter shall be the base subtype if scalar : ... : The rationale for this rule is that the user may declare a variable of : the base type, and write it to a stream. : ObjectAda chooses a 32 bit base integer in this case on Wintel : systems; apparently for "efficient arithmetic". This is certainly the crux of the problem. The base range is chosen to be a 32-bit range because that is the kind of arithmetic that is efficient. On many RISC architectures, that is the only kind of arithmetic, yet even on those architectures, one would presumably want types whose first subtype range fits easily in 16 bits to occupy no mor than 16 bits in the stream. So the real problem seems to be the connection between base range and stream representation. One possible solution is to "pretend" the base range is narrower for such types. The base range is just a minimum guaranteed range for arithmetic intermediates, but overflow need *not* always happen if you go outside the base range. For such a type it turns out that the base range would be pretty meaningless, except as it determines the number of stream elements per item written to the stream. Or equivalently, the "pretend" 16-bit base range would be checked by T'Write on output, and result in a Constraint_Error if exceeded, but the base range would be irrelevant pretty much everywhere else. Having a T'Stream_Size attribute is also a possible solution, but that requires the user to do extra work just to get the "expected" result, which is kind of annoying. -- -Tucker Taft **************************************************************** !section 13.13.2(17) !subject Stream size of 16 bit integers !reference RM95 13.13.2 (17) !reference 1998-15826.a Stephen Leake 98-03-14 !reference 1998-15827.a Tucker Taft 1998-3-17 !from Randy Brukardt 98-03-18 !keywords base type, size clause, stream representation !reference 1998-15828.a Randy Brukardt 1998-3-19>> !discussion We just ran into this problem. We were trying to use Streams to read Microsoft Windows bitmap files. Since the file format is defined external to Ada, and is not very friendly to Ada, it is fairly difficult. We tried various stearm solutions that worked on GNAT, but not on ObjectAda, because of the 16-bit vs. 32-bit problem. Since we can't control the stream representation, we can't use Streams to portably read this externally defined file format. We ended up reading an array of Storage_Elements, then converting that with Unchecked_Conversion. Yuk! This problem should be solved; otherwise, Ada 95 will not be able to portably read files with a definition outside of Ada. Randy. **************************************************************** From: Tucker Taft Sent: Friday, February 26, 1999 9:47 AM Subject: Re: Updated AIs AI 195 does not address an issue that we have confronted recently relating to stream attributes. It is very difficult to specify stream attributes for a limited private type, if you require them to be specified in the visible part. In general, you may not specify a "representation" attribute until after a type is fully defined. But if you also have to specify stream attributes in the visible part to make them usable, then you have a contradiction. There seem to be two solutions. One is to allow stream attributes to be specified before the type is fully defined. The other is to allow them to be specified in the private part. I suppose another option is to have some kind of "incomplete" stream attribute specification, such as "for Lim_Type'Read use <>;" in the visible part, and then complete the definition in the private part. This is rampant invention, of course, but it solves another problem. Types like Exception_Occurrence are required to have stream attributes, but not have any other primitive operations declared in the visible part. However stream attributes can only be defined in terms of some "normal" subprogram, which must necessarily also be visible at the point of the stream attribute definition. Another contradiction... > The update to AI > 195 is partial: I believe that Tucker owns the part of this AI that has to do > with the number of stream elements written/read for a scalar type. Okay, I will handle that part. -Tuck ****************************************************************