* Construction initialization problem @ 2008-12-04 16:08 Dmitry A. Kazakov 2008-12-04 17:35 ` Adam Beneschan 2008-12-04 22:17 ` Randy Brukardt 0 siblings, 2 replies; 8+ messages in thread From: Dmitry A. Kazakov @ 2008-12-04 16:08 UTC (permalink / raw) Consider this: type A is tagged limited null record; type B (X : not null access A'Class) is tagged limited null record; Now we want to specialize B so that its instances would already contain A: type C is new B with record Y : aliased A; end record; Now there seems no way to either create an object of C so that C.X would point to C.Y or else to derive from C a new type without discriminant: type D is new C (X => C.Y'Access) with null record; -- Illegal type D is new C (X => D.Y'Access) with null record; -- Illegal -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Construction initialization problem 2008-12-04 16:08 Construction initialization problem Dmitry A. Kazakov @ 2008-12-04 17:35 ` Adam Beneschan 2008-12-04 22:17 ` Randy Brukardt 1 sibling, 0 replies; 8+ messages in thread From: Adam Beneschan @ 2008-12-04 17:35 UTC (permalink / raw) On Dec 4, 8:08 am, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote: > Consider this: > > type A is tagged limited null record; > type B (X : not null access A'Class) is tagged limited null record; > > Now we want to specialize B so that its instances would already contain A: > > type C is new B with record > Y : aliased A; > end record; > > Now there seems no way to either create an object of C so that C.X would > point to C.Y or else to derive from C a new type without discriminant: > > type D is new C (X => C.Y'Access) with null record; -- Illegal > type D is new C (X => D.Y'Access) with null record; -- Illegal > On a related note, you can't do this either: type Rec1 is tagged null record; type Rec2 (D : access Rec1) is tagged limited record ... end record; type Rec3 is record F1 : aliased Rec1; F2 : Rec2 (Rec3.F1'access); end record; The rule preventing this is in 3.8, which says that the only component of "current instance" that you can use is a noninherited discriminant---and F1 is not a discriminant. But offhand, I don't think this restriction is necessary when the component is used as the prefix of 'Access. There shouldn't be any accessibility level issues, I think. Some care might be needed to get the language rules right in case the component is in a variant part, but other than that I can't think of any difficulties either in adding a rule to allow this or in implementation. Of course that doesn't mean there aren't any difficulties, just that I can't think of them. Simply adding that rule wouldn't quite solve Dmitry's problem, since in this case: type D is new C (X => D.Y'Access) with null record; -- Illegal he's trying to use a current instance in a discriminant, which a different clause in 3.8(12) makes illegal. Also, the issue of writing an allocator for type C whose discriminant refers to C would seem to require new syntax---maybe if we borrow the "extended return" idea? New_Obj := new ZZZ : C (X => ZZZ.Y'Access); -- Adam ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Construction initialization problem 2008-12-04 16:08 Construction initialization problem Dmitry A. Kazakov 2008-12-04 17:35 ` Adam Beneschan @ 2008-12-04 22:17 ` Randy Brukardt 2008-12-04 23:02 ` Adam Beneschan 2008-12-05 9:00 ` Dmitry A. Kazakov 1 sibling, 2 replies; 8+ messages in thread From: Randy Brukardt @ 2008-12-04 22:17 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:oscmgxrpod50$.g7h7snlssha0$.dlg@40tude.net... > Consider this: > > type A is tagged limited null record; > type B (X : not null access A'Class) is tagged limited null record; > > Now we want to specialize B so that its instances would already contain A: You never explained why you would want the discriminant in the first place. I would think that it would be better to do what you've said about C (include the instance directly in it) from the beginning, and the problem doesn't come up. Access discriminants have a fairly high overhead in Ada (because of the possibility of coextensions), so unless you *need* coextensions, they ought to be avoided. One could use a named access type instead to declare the discriminant if you really need a discriminant. But the only time you must use a discriminant is if you have a discriminant-dependent component, and there is no interesting way to make a component dependent on an access discriminant. So I'd prefer to just avoid the discriminant. If you're using discriminants to stand in for a proper constructor, I say "don't!!" Use a constructor function as the language intends (and force it with a (<>) if you need it). Randy. > type C is new B with record > Y : aliased A; > end record; > > Now there seems no way to either create an object of C so that C.X would > point to C.Y or else to derive from C a new type without discriminant: > > type D is new C (X => C.Y'Access) with null record; -- Illegal > type D is new C (X => D.Y'Access) with null record; -- Illegal > > -- > Regards, > Dmitry A. Kazakov > http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Construction initialization problem 2008-12-04 22:17 ` Randy Brukardt @ 2008-12-04 23:02 ` Adam Beneschan 2008-12-06 1:47 ` Randy Brukardt 2008-12-05 9:00 ` Dmitry A. Kazakov 1 sibling, 1 reply; 8+ messages in thread From: Adam Beneschan @ 2008-12-04 23:02 UTC (permalink / raw) On Dec 4, 2:17 pm, "Randy Brukardt" <ra...@rrsoftware.com> wrote: > "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote in messagenews:oscmgxrpod50$.g7h7snlssha0$.dlg@40tude.net... > > > Consider this: > > > type A is tagged limited null record; > > type B (X : not null access A'Class) is tagged limited null record; > > > Now we want to specialize B so that its instances would already contain A: > > You never explained why you would want the discriminant in the first place. > I would think that it would be better to do what you've said about C > (include the instance directly in it) from the beginning, and the problem > doesn't come up. > > Access discriminants have a fairly high overhead in Ada (because of the > possibility of coextensions), so unless you *need* coextensions, they ought > to be avoided. > > One could use a named access type instead to declare the discriminant if you > really need a discriminant. But the only time you must use a discriminant is > if you have a discriminant-dependent component, and there is no interesting > way to make a component dependent on an access discriminant. So I'd prefer > to just avoid the discriminant. I thought access discriminants---at least those with anonymous access type---were put into Ada 95 (before there were coextensions) to allow objects to point to other objects in a nested scope, in a restricted way (i.e. only in limited types) that prevented those references to a nested objects from staying around after the nested object disappeared. I wasn't aware that there were enough new features in Ada 2005 to provide this ability without using access discriminants. So what am I missing? -- Adam ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Construction initialization problem 2008-12-04 23:02 ` Adam Beneschan @ 2008-12-06 1:47 ` Randy Brukardt 0 siblings, 0 replies; 8+ messages in thread From: Randy Brukardt @ 2008-12-06 1:47 UTC (permalink / raw) "Adam Beneschan" <adam@irvine.com> wrote in message news:b5578996-6595-499c-8031-3bfae82a6070@s9g2000prm.googlegroups.com... ... > I thought access discriminants---at least those with anonymous access > type---were put into Ada 95 (before there were coextensions) to allow > objects to point to other objects in a nested scope, in a restricted > way (i.e. only in limited types) that prevented those references to a > nested objects from staying around after the nested object > disappeared. There always were coextensions in Ada 95; they just didn't have that name. You might be right about the original reason, but like everything else about anonymous access types, it doesn't work in practice. Best thing to do with them is to forget that they exist. > I wasn't aware that there were enough new features in > Ada 2005 to provide this ability without using access discriminants. > So what am I missing? I don't think this capability is (usefully) provided in Ada 95, either, because you don't want these things to be discriminants - that triggers all kinds of other rules (such as the requirement to use full record aggregates) that get in the way more than help. Coextensions probably are the only way to do this usefully, but they're very complex to implement and understand, and people don't use them anyway because they *look* like dynamic allocation (even though they are not supposed to be implemented that way). This is the sort of thing that looks good in toy examples, but falls apart when you actually try to use it (as Dmitry noted in his original question). Randy. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Construction initialization problem 2008-12-04 22:17 ` Randy Brukardt 2008-12-04 23:02 ` Adam Beneschan @ 2008-12-05 9:00 ` Dmitry A. Kazakov 2008-12-06 1:42 ` Randy Brukardt 1 sibling, 1 reply; 8+ messages in thread From: Dmitry A. Kazakov @ 2008-12-05 9:00 UTC (permalink / raw) On Thu, 4 Dec 2008 16:17:56 -0600, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:oscmgxrpod50$.g7h7snlssha0$.dlg@40tude.net... >> Consider this: >> >> type A is tagged limited null record; >> type B (X : not null access A'Class) is tagged limited null record; >> >> Now we want to specialize B so that its instances would already contain A: > > You never explained why you would want the discriminant in the first place. Huh, that is a mix-in, a major excuse for not to have multiple inheritance in Ada! > I would think that it would be better to do what you've said about C > (include the instance directly in it) from the beginning, and the problem > doesn't come up. No, in the case I have in mind, mix-in is appropriate. Consider layered network protocols. "A" were a transport layer. "B" were a protocol. There can be many different transports for the same protocol so an implementation of B shall dispatch to the operations of A. If B would inherit to A that would require permanent conversions to A'Class inside the operations of B (=a mess). Now consider that at some point you decided which transport (some type derived from A) and which protocol (some type derived from B) to take. You want to put it all into one opaque object. That would be the type C. But you cannot, without pointers and dynamic allocation. Welcome back to C++! > Access discriminants have a fairly high overhead in Ada (because of the > possibility of coextensions), so unless you *need* coextensions, they ought > to be avoided. Do you mean space or time overhead? > One could use a named access type instead to declare the discriminant if you > really need a discriminant. You mean an access component? But because Ada has no proper construction, there is no way to use not-null access components. OK, I know and in fact use one, but it is very intrusive. > But the only time you must use a discriminant is > if you have a discriminant-dependent component, and there is no interesting > way to make a component dependent on an access discriminant. So I'd prefer > to just avoid the discriminant. > > If you're using discriminants to stand in for a proper constructor, I say > "don't!!" Use a constructor function as the language intends (and force it > with a (<>) if you need it). Oh no, been there. The problem arose from inability to write such a function! You must be able to write an aggregate to return it from the function. The components of the aggregate will have the constraints impossible to spell. ---------------------------- The language problem is lack of abstraction. If there were abstract access types, then you could make an instance of A implement the interface of "access A" and them simply put an object of A as a discriminant for B: type AA is new A and access A with null record; -- A and access to A overriding function "'Access" (X : A) return access A; function Create return AA; -- Creates an instance of AA B_with_A : B (Create); -- Constrain it by an object -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Construction initialization problem 2008-12-05 9:00 ` Dmitry A. Kazakov @ 2008-12-06 1:42 ` Randy Brukardt 2008-12-06 10:16 ` Dmitry A. Kazakov 0 siblings, 1 reply; 8+ messages in thread From: Randy Brukardt @ 2008-12-06 1:42 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:d16z537mbee4$.wp9rmx0b7kjf.dlg@40tude.net... > On Thu, 4 Dec 2008 16:17:56 -0600, Randy Brukardt wrote: > >> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >> news:oscmgxrpod50$.g7h7snlssha0$.dlg@40tude.net... >>> Consider this: >>> >>> type A is tagged limited null record; >>> type B (X : not null access A'Class) is tagged limited null record; >>> >>> Now we want to specialize B so that its instances would already contain >>> A: >> >> You never explained why you would want the discriminant in the first >> place. > > Huh, that is a mix-in, a major excuse for not to have multiple inheritance > in Ada! A waste of effort. I don't believe in multiple inheritance (in any form); it seems to me to be an excuse for sloppy design. ... > No, in the case I have in mind, mix-in is appropriate. Consider layered > network protocols. "A" were a transport layer. "B" were a protocol. There > can be many different transports for the same protocol so an > implementation > of B shall dispatch to the operations of A. If B would inherit to A that > would require permanent conversions to A'Class inside the operations of B > (=a mess). I agree. I think it would be best to keep the two types totally separate. And use a function constructor to glue them together. > Now consider that at some point you decided which transport > (some type derived from A) and which protocol (some type derived from B) > to > take. You want to put it all into one opaque object. That would be the > type > C. I'm dubious that this is a good idea. But I think you could do so just fine with an appropriate constructor function. > But you cannot, without pointers and dynamic allocation. Welcome back to > C++! If you have to resort to a dynamically allocated component, just wrap it in a holder container. Then there is no possibility of data loss or (visible) dynamic allocation. It would be nice to have a way to access the object directly in such a container (a problem that I am working on for Amendment 2), but that is not a critical part to be able to do this. >> Access discriminants have a fairly high overhead in Ada (because of the >> possibility of coextensions), so unless you *need* coextensions, they >> ought >> to be avoided. > > Do you mean space or time overhead? Yes. :-) ... >> But the only time you must use a discriminant is >> if you have a discriminant-dependent component, and there is no >> interesting >> way to make a component dependent on an access discriminant. So I'd >> prefer >> to just avoid the discriminant. >> >> If you're using discriminants to stand in for a proper constructor, I say >> "don't!!" Use a constructor function as the language intends (and force >> it >> with a (<>) if you need it). > > Oh no, been there. The problem arose from inability to write such a > function! You must be able to write an aggregate to return it from the > function. The components of the aggregate will have the constraints > impossible to spell. The reason that we added extended return statements to the language is so that you don't have to write the entire return thing as an aggregate. The only reason you would have to do that is because you used a discriminant rather than a component. If it hurts, *don't do that*!! > ---------------------------- > The language problem is lack of abstraction. If there were abstract access > types, then you could make an instance of A implement the interface of > "access A" and them simply put an object of A as a discriminant for B: > > type AA is new A and access A with null record; -- A and access to A > overriding function "'Access" (X : A) return access A; > function Create return AA; -- Creates an instance of AA > B_with_A : B (Create); -- Constrain it by an object I don't understand this point at all. But it doesn't matter, because a function like your function "access" doesn't work: the accessibility will be wrong. Currently it would be library level, meaning that you could only return 'Unchecked_Access and essentially everything would be a dangling pointer. Tucker has a proposal to fix that (AI05-0051-1), but it requires a lot of additional runtime overhead for functions returning anonymous access types. That's probably not what you want, especially given that you could not do much with the result other than dereferencing it. (With Tucker's current proposal, 'Access would still always be illegal; that can be fixed to allow accessibility to the point of the call, but that doesn't help if you want to keep the access longer.) I've been trying to work on this problem, but the obvious solutions would require full dynamic accessibility checks, including passing the accessibility of all by-reference parameters -- and that is way too expensive to consider. Plus dynamic checks provide a new failure mechanism for code; it's not clear that is an advantage. I think that I might be able to fix the problem in the context of the containers only, and for dereference of the objects only, but it is not clear that the fix is worth the effort. Randy. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Construction initialization problem 2008-12-06 1:42 ` Randy Brukardt @ 2008-12-06 10:16 ` Dmitry A. Kazakov 0 siblings, 0 replies; 8+ messages in thread From: Dmitry A. Kazakov @ 2008-12-06 10:16 UTC (permalink / raw) On Fri, 5 Dec 2008 19:42:40 -0600, Randy Brukardt wrote: >> But you cannot, without pointers and dynamic allocation. Welcome back to >> C++! > > If you have to resort to a dynamically allocated component, just wrap it in > a holder container. That does not help. The inner container will have to propagate its constraint to the outer container. The only solution to this is to replace the outer container's constraint (anonymous not null access discriminant) with a pointer. Subsequently, that pointer cannot be a not-null pointer. > Then there is no possibility of data loss or (visible) > dynamic allocation. It would be nice to have a way to access the object > directly in such a container (a problem that I am working on for Amendment > 2), but that is not a critical part to be able to do this. Critical parts are: 1. an explicit use of heap 2. not-null constraint is lost It is just C++ written in Ada. >>> Access discriminants have a fairly high overhead in Ada (because of the >>> possibility of coextensions), so unless you *need* coextensions, they >>> ought to be avoided. >> >> Do you mean space or time overhead? > > Yes. :-) Can you elaborate on this. Considering: type T1 is ... procedure F1 (Object : in out T1); type T2 (X : not null access T1'Class) is ... procedure F2 (Object : T2) is begin Object.X.F1; -- Accessibility checks here? I hoped that the compiler should drop any checks because X is a not-null discriminant. >>> But the only time you must use a discriminant is >>> if you have a discriminant-dependent component, and there is no >>> interesting >>> way to make a component dependent on an access discriminant. So I'd >>> prefer >>> to just avoid the discriminant. >>> >>> If you're using discriminants to stand in for a proper constructor, I say >>> "don't!!" Use a constructor function as the language intends (and force >>> it >>> with a (<>) if you need it). >> >> Oh no, been there. The problem arose from inability to write such a >> function! You must be able to write an aggregate to return it from the >> function. The components of the aggregate will have the constraints >> impossible to spell. > > The reason that we added extended return statements to the language is so > that you don't have to write the entire return thing as an aggregate. The > only reason you would have to do that is because you used a discriminant > rather than a component. If it hurts, *don't do that*!! I don't see how a function may help here. Let us rewrite the stuff to pointers as you suggest: type A is tagged limited null record; type A_Ptr is not null access all A'Class; type B is tagged limited record X : A_Ptr; end record; type C is new B with record Y : aliased A; end record; function Create return C is begin return Result : C do Result.X := Result.Y'Unchecked_Access; end return; end Create; This will propagate Constraint_Error upon an attempt to create a C with Create. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2008-12-06 10:16 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2008-12-04 16:08 Construction initialization problem Dmitry A. Kazakov 2008-12-04 17:35 ` Adam Beneschan 2008-12-04 22:17 ` Randy Brukardt 2008-12-04 23:02 ` Adam Beneschan 2008-12-06 1:47 ` Randy Brukardt 2008-12-05 9:00 ` Dmitry A. Kazakov 2008-12-06 1:42 ` Randy Brukardt 2008-12-06 10:16 ` Dmitry A. Kazakov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox