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,a5d76152c5cb8790 X-Google-Attributes: gid103376,public From: Matthew Heaney Subject: Re: class-wide objects Date: 1998/12/07 Message-ID: X-Deja-AN: 419470470 Sender: matt@mheaney.ni.net References: <366B1190.C8142307@magic.fr> NNTP-Posting-Date: Sun, 06 Dec 1998 17:38:10 PDT Newsgroups: comp.lang.ada Date: 1998-12-07T00:00:00+00:00 List-Id: Francois Godme writes: > Since the introduction of class-wide objects, support for dynamically > sized objects, as far as I know and understand Ada95, has dropped down. > Correct me if I'm wrong. Ada95 support for dynamically sized objects is > now incomplete to allow pointer-free code because Ada95 doesn't provide > a mechanism to gain information on the actual tagged object suitable to > define a subtype. > > Ada95 allows a local variable to be constrained by the actual tagged > parameter but in an incomplete way: > > procedure P3 (S : in Point'Class; > N : in Natural) is > > Local : Point'Class := S; -- But what if type point is limited ? > > begin > ... > end P3; But the example you give is not specific to Ada95. Consider this untagged type: package Points is type Point is limited private; ... end Points; procedure Op (P : in Point) is Local : Point := P; -- illegal, because P is limited begin Of course, the type as declared isn't indefinite, so you could do this: procedure Op (P : in Point) is Local : Point; begin Local := P; -- illegal but that too is illegal, because you just moved assignment into the executable region. The fact that something is tagged makes no difference. It is limitedness and indefiniteness that is the cause of your problem. To solve you problem, you have a couple of choices: 1) Make the type non-limited. This gives you assignment. This is exactly the approach I took in my example of combining active iteration with a factory method. For example: procedure Copy (From : in Stack_Type'Class; To : in out Stack_Type) is Iterator : Iterator_Type'Class := Start_At_Bottom (From); begin The iterator object has a class-wide type, and is therefore indefinite, which means it requires initialization during its elaboration. But this is only possible because all types in the iterator class are non-limited. The complete text of my example is at the Patterns archive at the ACM, under the Nov 1998 link. The subject is "Iterator and Factory Method Patterns Combined." >From the text of the example: "The declaration of a class-wide object requires that the object be given an initial value, because a class-wide type is indefinite. (In our case, the initial value is supplied by the factory method constructor.) This means we need assignment for the type, and therefore the iterator must be declared as non-limited." 2) Put the object on the heap. Your type will probably have to have some sort of dispatching clone method: generic ... package Stacks is type Stack_Type is tagged limited private; type Stack_Access is access all Stack_Type'Class; function Copy (Stack : Stack_Type) return Stack_Access; ... end Stacks; which you'd use something like: procedure Op (Stack : in Stack_Type'Class) is Copy_Of_Stack : Stack_Access := Copy (Stack); begin This is ugly, because you have to use heap to make the copy. If you really need to copy class-wide objects, then you should try to make the type non-limited, as in 1) above. This is not an Ada95 problem. It is an Ada problem. The problem is that Ada doesn't separate the notions of initialization and assignment. Functions that return a value of the type are used as constructors, instead of having a dedicated constructor operation. Normally, this isn't a problem, but it is when you want to copy a limited (and indefinite) type. But... if you're trying to copy a type that's limited, then what's the point of making it limited? Making something limited means you don't want assignment. I've advocated adding true constructors to the language, something like: type Stack_Type is tagged limited private; constructor Copy (Stack : Stack_Type) return Stack_Type; that you'd use like this: procedure Op (Stack : in Stack_Type'Class) is Copy_Of_Stack : Stack_Type'Class'Copy (Stack); begin This way, you can initialize a limited and indefinite object during its elaboration. Others have suggested a more conservative approach, allowing functions to be used in the declarative region only: procedure Op (Stack : in Stack_Type'Class) is Copy_Of_Stack : Stack_Type'Class := Copy (Stack); begin > An attribute to get the subtype of the actual tagged parameter would > solve both problems. May be we could combine > the two attributes 'Class and 'Tag together: > > procedure P4 (S : in Point'Class; > N : in Natural) is > > Local : Point'Class (S'Tag); -- allocates an object with the tag of S > > subtype T_Local_Point is Point'Class (S'Tag); > > Local_Array : array (1 .. N) of T_Local_Point; > > begin > ... > end P4; > > What do you think ? Am I right or am I missing something ? Could the > GNAT team provide an implementation defined attribute for this ? I don't think the GNAT team is in the business of extending or even fixing the language. (Although you could argue T'Unrestricted_Access does just that.) If you want to initialize an indefinite type (during its elaboration), then make the type non-limited. If you want a sequence of indefinite objects, then don't use an array. Use a more abstract, linear data structure as a queue, in combination with an iterator that provides sequential (or even indexed) access to the items.