* Preventing private procedure visibility being made public through extension @ 2017-05-20 17:33 Jere 2017-05-20 20:13 ` AdaMagica ` (2 more replies) 0 siblings, 3 replies; 42+ messages in thread From: Jere @ 2017-05-20 17:33 UTC (permalink / raw) I tried to whittle this down to a small example, so forgive the uselessness of the example itself. I'm extending a library with some of my own components and I want (if possible) to allow my components to interact with the other library components as seamlessly as possible. Say a library provides a tagged type with the following procedure: Base.ads ************************************************* package Base is type Base_Param is tagged null record; type Base_Type is tagged null record; procedure Something (Obj : in out Base_Type; Value : Base_Param'Class) is null; end Base; ************************************************* keep in mind declaring the procedure as "is null" is just for example sake. In real life it would have a body. I want to extend both of the types in that package to do some extra processing. In particular, I want to override the procedure Something so that it does more than the version for Base_Type and so it takes in my derived param class as a parameter. I also want to prevent a client from calling the inherited version of the procedure Something as it would bypass the code I have in my new prototype of the procedure. I declare the package like this: Derived.ads ************************************************* with Base; package Derived is type Derived_Param is new Base.Base_Param with null record; type Derived_Type is new Base.Base_Type with private; procedure Something (Obj : in out Derived_Type; Value : Derived_Param'Class) is null; type More_Derived_Type is new Derived_Type with private; private type Derived_Type is new Base.Base_Type with null record; overriding procedure Something (Obj : in out Derived_Type; Value : Base.Base_Param'Class) is null; type More_Derived_Type is new Derived_Type with null record; end Derived; ************************************************* Again, keep in mind this is a simplified example. The procedures would do something and the extended types would have other params. This is all well and good. Variables of Derived_Type can only call the version of something that I specified publicly and the overriden one cannot be called. However, variables of More_Derived_Type have public visibility to both versions of Something. I would have hoped that the version hidden by Derived_Type would keep it hidden, but extending the type makes the private procedure visible again. Example main: main.adb ************************************************* with Base; with Derived; use Derived; procedure Main is p : Derived_Param; d : Derived_Type; m : More_Derived_Type; begin d.Something(p); -- Works as expected m.Something(p); -- Causes an error due to ambiguity end Main; ************************************************* Is there a way for me to prevent extending types from making the procedure public again? The error is ambiguous call to "Something" ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-20 17:33 Preventing private procedure visibility being made public through extension Jere @ 2017-05-20 20:13 ` AdaMagica 2017-05-20 21:55 ` Jere 2017-05-20 20:32 ` Dmitry A. Kazakov 2017-05-21 18:14 ` Robert Eachus 2 siblings, 1 reply; 42+ messages in thread From: AdaMagica @ 2017-05-20 20:13 UTC (permalink / raw) > with Base; > > package Derived is > > type Derived_Param is new Base.Base_Param with null record; > > type Derived_Type is new Base.Base_Type with private; > > procedure Something > (Obj : in out Derived_Type; > Value : Derived_Param'Class) > is null; > > private > > type Derived_Type is new Base.Base_Type with null record; > > overriding > procedure Something > (Obj : in out Derived_Type; > Value : Base.Base_Param'Class) > is null; > > end Derived; Here you are wrong. Hiding the overriding does not prevent the client from calling the operation. It nearly makes no difference whether you override an inherited operation in the public or in the private part. (There are a few syntactic diffrences though.) > ************************************************* > > This is all well and good. Variables of Derived_Type > can only call the version of something that I specified > publicly and the overriden one cannot be called. As I said, this is wrong. > > However, variables of More_Derived_Type have public > visibility to both versions of Something. I would > have hoped that the version hidden by Derived_Type > would keep it hidden, but extending the type makes > the private procedure visible again. > > Example main: > > main.adb > ************************************************* > with Base; > with Derived; use Derived; > > procedure Main is > p : Derived_Param; > d : Derived_Type; > m : More_Derived_Type; > begin > d.Something(p); -- Works as expected I think your compiler is in error here. This call is (IMHO) also ambiguous. Both of these operations are visible here: not overriding procedure Something (Obj : in out Derived_Type; Value : Derived_Param'Class); overriding procedure Something (Obj : in out Derived_Type; Value : Base.Base_Param'Class); Since your parameter d belongs to both classwide types, it's impossible to decide to which class it has to be converted (implicitly). > m.Something(p); -- Causes an error due to ambiguity > end Main; > ************************************************* > > Is there a way for me to prevent extending types from making > the procedure public again? > > The error is ambiguous call to "Something" ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-20 20:13 ` AdaMagica @ 2017-05-20 21:55 ` Jere 0 siblings, 0 replies; 42+ messages in thread From: Jere @ 2017-05-20 21:55 UTC (permalink / raw) On Saturday, May 20, 2017 at 4:13:10 PM UTC-4, AdaMagica wrote: > > with Base; > > > > package Derived is > > > > type Derived_Param is new Base.Base_Param with null record; > > > > type Derived_Type is new Base.Base_Type with private; > > > > procedure Something > > (Obj : in out Derived_Type; > > Value : Derived_Param'Class) > > is null; > > > > private > > > > type Derived_Type is new Base.Base_Type with null record; > > > > overriding > > procedure Something > > (Obj : in out Derived_Type; > > Value : Base.Base_Param'Class) > > is null; > > > > end Derived; > > Here you are wrong. Hiding the overriding does not prevent the client from calling the operation. It nearly makes no difference whether you override an inherited operation in the public or in the private part. (There are a few syntactic diffrences though.) > > > ************************************************* > > > > This is all well and good. Variables of Derived_Type > > can only call the version of something that I specified > > publicly and the overriden one cannot be called. > > As I said, this is wrong. > > > > > However, variables of More_Derived_Type have public > > visibility to both versions of Something. I would > > have hoped that the version hidden by Derived_Type > > would keep it hidden, but extending the type makes > > the private procedure visible again. > > > > Example main: > > > > main.adb > > ************************************************* > > with Base; > > with Derived; use Derived; > > > > procedure Main is > > p : Derived_Param; > > d : Derived_Type; > > m : More_Derived_Type; > > begin > > d.Something(p); -- Works as expected > > I think your compiler is in error here. This call is (IMHO) also ambiguous. > Both of these operations are visible here: Perhaps a compiler bug, but it is even more than just not ambiguous, it cannot call the version of the procedure that I put in the private section (for variables of type Derived_Type). If I declare a variable: b : Base.Base_Param; and try to call d.Something(b); It will fail with an error: Expected type "Derived_Param'Class" defined at derived.ads:5 expected type "Derived_Param'Class" defined at derived.ads:5 found type "Base_Param" defined at base.ads:3 I even tried casting b to Base_Param'Class with similar results (compiler error). It is acting like the procedure is not publicly visible for variable d. Now if I didn't have a procedure named Something declared, it acts as you described. Is this a compiler bug? ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-20 17:33 Preventing private procedure visibility being made public through extension Jere 2017-05-20 20:13 ` AdaMagica @ 2017-05-20 20:32 ` Dmitry A. Kazakov 2017-05-20 22:51 ` Jere 2017-05-22 21:12 ` Randy Brukardt 2017-05-21 18:14 ` Robert Eachus 2 siblings, 2 replies; 42+ messages in thread From: Dmitry A. Kazakov @ 2017-05-20 20:32 UTC (permalink / raw) On 2017-05-20 19:33, Jere wrote: > package Derived is > > type Derived_Param is new Base.Base_Param with null record; > > type Derived_Type is new Base.Base_Type with private; > > procedure Something > (Obj : in out Derived_Type; > Value : Derived_Param'Class) > is null; > > type More_Derived_Type is new Derived_Type with private; > > private > > type Derived_Type is new Base.Base_Type with null record; > > overriding > procedure Something > (Obj : in out Derived_Type; > Value : Base.Base_Param'Class) > is null; This is not private regardless where it appears. When you derive from Base_Type that automatically declares Something for Derived_Type. You may override its body but that is all you can do. When you declare another Something for Derived_Param'Class that overloads the already inherited Something and here you are. > Is there a way for me to prevent extending types from making > the procedure public again? It is public. That was the decision made in the package Base. The package Derived has no say on that. ---------------------------------- The actual problem you have is parallel types hierarchies. You want to derive tied instances of Base_Type'Class and Base_Param'Class. Base_Type ----- Base_Param | | Derived_Type -- Derived_Param This requires 1. Full multiple dispatch 2. Dispatch constrained to certain combinations (parallel hierarchies) This is not supported in Ada (or in any other OO language I am aware of) A typical workaround is replacing multiple dispatch (pos.1) with a cascaded dispatch. I.e. you keep Something class-wide in the second parameter. Then you override it as you did. In the body you select the parameter type in any desired way (second dispatch). The drawback is obviously loss of static type checks (pos.2): procedure Something ( Obj : in out Derived_Type; Value : Base_Param'Class ) is begin if not Value in Derived_Param then -- or a more relaxed -- Derived_Param'Class raise Constraint_Error with "Value type error"; end if; declare Checked_Value : Derived_Param renames Derived_Param (Value); begin ... -- Go on -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-20 20:32 ` Dmitry A. Kazakov @ 2017-05-20 22:51 ` Jere 2017-05-21 0:51 ` Jere 2017-05-21 8:44 ` Dmitry A. Kazakov 2017-05-22 21:12 ` Randy Brukardt 1 sibling, 2 replies; 42+ messages in thread From: Jere @ 2017-05-20 22:51 UTC (permalink / raw) On Saturday, May 20, 2017 at 4:32:07 PM UTC-4, Dmitry A. Kazakov wrote: > On 2017-05-20 19:33, Jere wrote: > > Is there a way for me to prevent extending types from making > > the procedure public again? > > It is public. That was the decision made in the package Base. The > package Derived has no say on that. > > ---------------------------------- > The actual problem you have is parallel types hierarchies. You want to > derive tied instances of Base_Type'Class and Base_Param'Class. > > Base_Type ----- Base_Param > | | > Derived_Type -- Derived_Param > > This requires > > 1. Full multiple dispatch > 2. Dispatch constrained to certain combinations (parallel hierarchies) > > This is not supported in Ada (or in any other OO language I am aware of) If I understand your definitions correctly, C++ supports this. It can fully dispatch on any number of types since dispatch is tied to the function and not the type itself. C++ does have its own problems though. I do know that C++ supports hiding of functions by making them private in the extending class. I was hoping Ada did as well. I don't mind if dispatching runs the private versions, I can protect against that, but I didn't want clients using the methods as they break operation of the extended class. I can implement your suggestion from below though. I don't like the loss of static type checking here, but if Ada has not viable options here, then that may be what I have to do. My real issue is with how the constructors for the tagged type in the library are done. I want my derived type to work with the constructors defined in the library, which means it has to extend one of the types defined in the library itself. But I don't want clients to extend my class and use the inherited constructors from the base class because they'll break my extended class. My alternate solution was to use composition, but it really feels inelegant in this case. I did: package Derived is type Base_Access is access all Base.Base_Type; type Derived_Type is tagged limited private; procedure Something (Obj : Derived_Type; Value : Derived_Param'Class) is null; procedure Get_Access(Obj : Derived_Type) return Base_Access; private type Derived_Type is tagged limited record Param : Base.Base_Type; end record; end Derived; and Get_Access just returns Param'Unchecked_Access. When I want to use the library methods, I use that to pass back the correct type for them. I really don't like doing this. I don't like using access types for one. It's also doesn't feel like a very clean way because you have to do something out of the ordinary just to use the class like it was meant to be. The actual use case is Gnoga. All of the constructors (the Create procedures) require parent to be of type Gnoga.Gui.Base.Base_Type'Class, but I really want my Dialog_Type constructor to only accept parents of type Gnoga.Gui.Window.Window_Type'Class. But it also has to publicly extend Gnoga.Gui.Base.Base_Type (or I use Gnoga.Gui.View.View_Base_Type) so that other controls can create themselves in it. This opens my type and types that extend it to being thrashed by calls to Create_From_HTML and so on. Similar to your suggestion, I can override them and raise an exception...it just feels clunky. I was just hoping for something better from a static checking perspective. There was a discussion in the Gnoga mailing list if you are wanting more concrete examples. I was just simplifying it for here to see if there was a language defined way to get what I needed statically. It sounds like there isn't. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-20 22:51 ` Jere @ 2017-05-21 0:51 ` Jere 2017-05-21 9:16 ` Chris Moore 2017-05-21 8:44 ` Dmitry A. Kazakov 1 sibling, 1 reply; 42+ messages in thread From: Jere @ 2017-05-21 0:51 UTC (permalink / raw) On Saturday, May 20, 2017 at 6:51:43 PM UTC-4, Jere wrote: > On Saturday, May 20, 2017 at 4:32:07 PM UTC-4, Dmitry A. Kazakov wrote: > > On 2017-05-20 19:33, Jere wrote: > > > Is there a way for me to prevent extending types from making > > > the procedure public again? > > > > It is public. That was the decision made in the package Base. The > > package Derived has no say on that. > > > > ---------------------------------- > > The actual problem you have is parallel types hierarchies. You want to > > derive tied instances of Base_Type'Class and Base_Param'Class. > > > > Base_Type ----- Base_Param > > | | > > Derived_Type -- Derived_Param > > > The actual use case is Gnoga. All of the constructors > (the Create procedures) require parent to be of type > Gnoga.Gui.Base.Base_Type'Class, but I really want my > Dialog_Type constructor to only accept parents of type > Gnoga.Gui.Window.Window_Type'Class. But it also has > to publicly extend Gnoga.Gui.Base.Base_Type > (or I use Gnoga.Gui.View.View_Base_Type) so that > other controls can create themselves in it. This > opens my type and types that extend it to being > thrashed by calls to Create_From_HTML and so on. > Similar to your suggestion, I can override them and > raise an exception...it just feels clunky. > Or perhaps if Ada supported constructors that were not dispatchable and that were not inherited that might also work. I would need to think about it a bit to be sure about that though. Just an off the cuff thought. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 0:51 ` Jere @ 2017-05-21 9:16 ` Chris Moore 2017-05-21 22:55 ` Jere 0 siblings, 1 reply; 42+ messages in thread From: Chris Moore @ 2017-05-21 9:16 UTC (permalink / raw) On 21/05/2017 01:51, Jere wrote: > Or perhaps if Ada supported constructors that were not > dispatchable and that were not inherited that might > also work. I would need to think about it a bit > to be sure about that though. Just an off the cuff > thought. If you put the constructors in child packages then they aren't inherited. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 9:16 ` Chris Moore @ 2017-05-21 22:55 ` Jere 0 siblings, 0 replies; 42+ messages in thread From: Jere @ 2017-05-21 22:55 UTC (permalink / raw) On Sunday, May 21, 2017 at 5:16:32 AM UTC-4, Chris Moore wrote: > On 21/05/2017 01:51, Jere wrote: > > > Or perhaps if Ada supported constructors that were not > > dispatchable and that were not inherited that might > > also work. I would need to think about it a bit > > to be sure about that though. Just an off the cuff > > thought. > > If you put the constructors in child packages then they aren't inherited. Yes, I agree. In my case, the base type is already provided and the constructing procedures are in the same package, so that is what is causing my initial problem. I don't have control over the library unfortunately. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-20 22:51 ` Jere 2017-05-21 0:51 ` Jere @ 2017-05-21 8:44 ` Dmitry A. Kazakov 2017-05-21 12:19 ` J-P. Rosen 2017-05-21 20:06 ` Jere 1 sibling, 2 replies; 42+ messages in thread From: Dmitry A. Kazakov @ 2017-05-21 8:44 UTC (permalink / raw) On 2017-05-21 00:51, Jere wrote: > On Saturday, May 20, 2017 at 4:32:07 PM UTC-4, Dmitry A. Kazakov wrote: >> On 2017-05-20 19:33, Jere wrote: >>> Is there a way for me to prevent extending types from making >>> the procedure public again? >> >> It is public. That was the decision made in the package Base. The >> package Derived has no say on that. >> >> ---------------------------------- >> The actual problem you have is parallel types hierarchies. You want to >> derive tied instances of Base_Type'Class and Base_Param'Class. >> >> Base_Type ----- Base_Param >> | | >> Derived_Type -- Derived_Param >> >> This requires >> >> 1. Full multiple dispatch >> 2. Dispatch constrained to certain combinations (parallel hierarchies) >> >> This is not supported in Ada (or in any other OO language I am aware of) > > If I understand your definitions correctly, C++ supports this. It can > fully dispatch on any number of types since dispatch is tied to the > function and not the type itself. No. Dispatch cannot be tied to anything but a parameter/result (single dispatch) or a combination of (multiple dispatch), just per definition of. In C++ the dispatching parameter is to the first parameter specified in the dotted notation: object.foo(). > I do know that C++ supports hiding of functions by making them private > in the extending class. No. That is not possible in C++ either. A public method cannot be made private. In general from the OO point of view, a method cannot be removed because it is a property of the class and not of an individual type. It is the contract of the class to have a method Foo. All instances of the class must have this method. If they don't they are not instances of. Period. [To support method disallowing the language must support ad-hoc superclasses, so that one could split an original class into parts to chip the undesired operation away from one part and put the new type in that part instead of the whole original class.] > My real issue is with how the constructors for the tagged type in > the library are done. I want my derived type to work with the > constructors defined in the library, which means it has to extend > one of the types defined in the library itself. But I don't want > clients to extend my class and use the inherited constructors from > the base class because they'll break my extended class. These are not constructors proper. Maybe you meant "constructing function". There is a huge difference. Anyway a constructing function being a primitive operation must work, exactly because this is an primitive operation which gets overridden. So works a constructor proper which is not an operation and cannot be called explicitly and when invoked, then implicitly always with its own type. What you have is neither, it is probably an operation to initialize a part of the object. Such things are usually made private [to be used in a public constructing function]. [Yes it is a huge problem for library designers that Ada does not have constructors. Alas, there is no desire to fix that] > My alternate solution was to use composition, but it really feels > inelegant in this case. I did: > > package Derived is > > type Base_Access is access all Base.Base_Type; > > type Derived_Type is tagged limited private; > > procedure Something > (Obj : Derived_Type; > Value : Derived_Param'Class) > is null; > > procedure Get_Access(Obj : Derived_Type) return Base_Access; procedure Get_Access (Obj : Derived_Type) return not null access Base_Type; > private > > type Derived_Type is tagged limited record > Param : Base.Base_Type; Param : aliased Base_Type; > end record; > > end Derived; > > and Get_Access just returns Param'Unchecked_Access. > When I want to use the library methods, I use that to > pass back the correct type for them. > > I really don't like doing this. I don't like using > access types for one. It's also doesn't feel like > a very clean way because you have to do something > out of the ordinary just to use the class like it was > meant to be. Aggregation + delegation is a decent thing. If you used anonymous access you would reduce dangers of pointers to zero. [If Ada supported return by-reference (it existed once in a rudimentary form and was removed in Ada 2005), the construct would be even better.] [If Ada supported interface inheritance, you could inherit interface of Base_Type and use Derived_Type where Base_Type is expected calling to Get_Access implicitly] But wait ... you can still call Something on the Base_Type part of Derived_Type. So, this resolves exactly *nothing*. The problem is in the Base_Type'Class. > The actual use case is Gnoga. All of the constructors > (the Create procedures) require parent to be of type > Gnoga.Gui.Base.Base_Type'Class, but I really want my > Dialog_Type constructor to only accept parents of type > Gnoga.Gui.Window.Window_Type'Class. But it also has > to publicly extend Gnoga.Gui.Base.Base_Type > (or I use Gnoga.Gui.View.View_Base_Type) so that > other controls can create themselves in it. This > opens my type and types that extend it to being > thrashed by calls to Create_From_HTML and so on. > Similar to your suggestion, I can override them and > raise an exception...it just feels clunky. Believe other methods I know and have tried, e.g. involving generics, are far worse. > I was just hoping for something better from a static checking > perspective. There was a discussion in the Gnoga mailing > list if you are wanting more concrete examples. I was just > simplifying it for here to see if there was a language > defined way to get what I needed statically. It sounds like > there isn't. GUI is one of classic examples where multiple dispatch is required. It is a fact of reality which cannot be worked around without compromising something (no pun intended (:-)). -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 8:44 ` Dmitry A. Kazakov @ 2017-05-21 12:19 ` J-P. Rosen 2017-05-21 12:53 ` Dmitry A. Kazakov 2017-05-21 20:06 ` Jere 1 sibling, 1 reply; 42+ messages in thread From: J-P. Rosen @ 2017-05-21 12:19 UTC (permalink / raw) Le 21/05/2017 à 10:44, Dmitry A. Kazakov a écrit : > If Ada supported return by-reference (it existed once in a rudimentary > form and was removed in Ada 2005) More precisely: an implicit, hard to understand return by reference was replaced with an explicit one. If you want return by reference, have a function that returns "access T". -- J-P. Rosen Adalog 2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00 http://www.adalog.fr ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 12:19 ` J-P. Rosen @ 2017-05-21 12:53 ` Dmitry A. Kazakov 0 siblings, 0 replies; 42+ messages in thread From: Dmitry A. Kazakov @ 2017-05-21 12:53 UTC (permalink / raw) On 2017-05-21 14:19, J-P. Rosen wrote: > Le 21/05/2017 à 10:44, Dmitry A. Kazakov a écrit : >> If Ada supported return by-reference (it existed once in a rudimentary >> form and was removed in Ada 2005) > More precisely: an implicit, hard to understand return by reference was > replaced with an explicit one. If you want return by reference, have a > function that returns "access T". Well, anonymous access is not same as reference. 1. It lacks implicit dereferencing 2. It overrides assignment operation of T with its own, which never should be there in first place 3. It does not transfer the access mode to T. E.g. the out mode of a reference to T applies to T itself". For an access type it applies to the type access T. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 8:44 ` Dmitry A. Kazakov 2017-05-21 12:19 ` J-P. Rosen @ 2017-05-21 20:06 ` Jere 2017-05-21 21:07 ` Dmitry A. Kazakov 2017-05-22 21:17 ` Randy Brukardt 1 sibling, 2 replies; 42+ messages in thread From: Jere @ 2017-05-21 20:06 UTC (permalink / raw) On Sunday, May 21, 2017 at 4:44:27 AM UTC-4, Dmitry A. Kazakov wrote: > On 2017-05-21 00:51, Jere wrote: > > On Saturday, May 20, 2017 at 4:32:07 PM UTC-4, Dmitry A. Kazakov wrote: > >> On 2017-05-20 19:33, Jere wrote: > > I do know that C++ supports hiding of functions by making them private > > in the extending class. > > No. That is not possible in C++ either. A public method cannot be made > private. > > In general from the OO point of view, a method cannot be removed because > it is a property of the class and not of an individual type. It is the > contract of the class to have a method Foo. All instances of the class > must have this method. If they don't they are not instances of. Period. > > [To support method disallowing the language must support ad-hoc > superclasses, so that one could split an original class into parts to > chip the undesired operation away from one part and put the new type in > that part instead of the whole original class.] > The language supports making them private in the context of the extending classes. Take for example: class Base{ public: class Base_Param{}; void Something(Base_Param Obj){} }; class Derived : public Base { private: void Something(Base_Param Obj); }; class MoreDerived : public Derived{}; The following will not compile: void run_test(){ Base::Base_Param b; Derived d; d.Something(b); } nor will void run_test(){ Base::Base_Param b; MoreDerived m; m.Something(b); } both will fail with the error: "Something" is a private member of Derived I'm not saying it is the right design, but it is possible. You can always get around it by casting down to type Base (where the function is public), but that is an explicit action that takes intent. A user just creating a type can't use the function out of the box because, as the error indicates, it is private for Derived (and for MoreDerived by extension). > > My real issue is with how the constructors for the tagged type in > > the library are done. I want my derived type to work with the > > constructors defined in the library, which means it has to extend > > one of the types defined in the library itself. But I don't want > > clients to extend my class and use the inherited constructors from > > the base class because they'll break my extended class. > > These are not constructors proper. Maybe you meant "constructing > function". There is a huge difference. > > Anyway a constructing function being a primitive operation must work, > exactly because this is an primitive operation which gets overridden. So > works a constructor proper which is not an operation and cannot be > called explicitly and when invoked, then implicitly always with its own > type. > > What you have is neither, it is probably an operation to initialize a > part of the object. Such things are usually made private [to be used in > a public constructing function]. > > [Yes it is a huge problem for library designers that Ada does not have > constructors. Alas, there is no desire to fix that] You're right, they are not proper constructors. I haven't found anything in Ada yet that gives me that. At best they are initializers. > > I really don't like doing this. I don't like using > > access types for one. It's also doesn't feel like > > a very clean way because you have to do something > > out of the ordinary just to use the class like it was > > meant to be. > > Aggregation + delegation is a decent thing. > > If you used anonymous access you would reduce dangers of pointers to zero. > Definitely. I am also worried about someone copying the access value and holding onto it past the lifetime of the object. I try to avoid Access types when I can. I wish there was someway to actually pass a limited type as a function return that didn't involve build in place that can initialize. I just want to be able to return the limited object out temporarily to access it: Derived.Get_Base.Some_Procedure but not allow initialization when I don't intend the function to do that: Temp : Base := Derived.Get_Base; -- didn't really want to allow As it stands the only option I came up with is something kludgy like: type Derived_Public is tagged limited record Parent : Base_Type; end record; -- This is the workhorse type type Derived is new Derived_Public with private; -- This type is purely for constructor like procedures type Derived_With_Constructors is new Derived with private; procedure Constructing_Procedure (Obj : in out Derived_With_Constructors); Then people extending it just have to do: type More_Derived is new Derived with private; private type More_Derived is new Derived_With_Constructors with null record; That way, extenders don't publicly provide the Constructor procedures, but have access to them privately. Additionally, I get the Base type via composition so it's procedures won't stomp on Derived types and I can still use it in the library. There's probably a better way, but that is what I came up with. I don't like it though. > > The actual use case is Gnoga. All of the constructors > > (the Create procedures) require parent to be of type > > Gnoga.Gui.Base.Base_Type'Class, but I really want my > > Dialog_Type constructor to only accept parents of type > > Gnoga.Gui.Window.Window_Type'Class. But it also has > > to publicly extend Gnoga.Gui.Base.Base_Type > > (or I use Gnoga.Gui.View.View_Base_Type) so that > > other controls can create themselves in it. This > > opens my type and types that extend it to being > > thrashed by calls to Create_From_HTML and so on. > > Similar to your suggestion, I can override them and > > raise an exception...it just feels clunky. > > Believe other methods I know and have tried, e.g. involving generics, > are far worse. That's been my experience as well. > > I was just hoping for something better from a static checking > > perspective. There was a discussion in the Gnoga mailing > > list if you are wanting more concrete examples. I was just > > simplifying it for here to see if there was a language > > defined way to get what I needed statically. It sounds like > > there isn't. > > GUI is one of classic examples where multiple dispatch is required. It > is a fact of reality which cannot be worked around without compromising > something (no pun intended (:-)). > Thanks for the responses! ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 20:06 ` Jere @ 2017-05-21 21:07 ` Dmitry A. Kazakov 2017-05-21 22:28 ` Jere 2017-05-22 13:43 ` AdaMagica 2017-05-22 21:17 ` Randy Brukardt 1 sibling, 2 replies; 42+ messages in thread From: Dmitry A. Kazakov @ 2017-05-21 21:07 UTC (permalink / raw) On 2017-05-21 22:06, Jere wrote: > On Sunday, May 21, 2017 at 4:44:27 AM UTC-4, Dmitry A. Kazakov wrote: >> On 2017-05-21 00:51, Jere wrote: >>> On Saturday, May 20, 2017 at 4:32:07 PM UTC-4, Dmitry A. Kazakov wrote: >>>> On 2017-05-20 19:33, Jere wrote: >>> I do know that C++ supports hiding of functions by making them private >>> in the extending class. >> >> No. That is not possible in C++ either. A public method cannot be made >> private. >> >> In general from the OO point of view, a method cannot be removed because >> it is a property of the class and not of an individual type. It is the >> contract of the class to have a method Foo. All instances of the class >> must have this method. If they don't they are not instances of. Period. >> >> [To support method disallowing the language must support ad-hoc >> superclasses, so that one could split an original class into parts to >> chip the undesired operation away from one part and put the new type in >> that part instead of the whole original class.] >> > The language supports making them private in the context of the > extending classes. Take for example: > > class Base{ > public: > class Base_Param{}; > > void Something(Base_Param Obj){} Something is not a method here. If you make it a method [= Ada's primitive operation]: virtual void Something (Base_Param Obj); then your code will stop compiling. In Ada a non-primitive operation could be disallowed by declaring it abstract, just the same. An Ada example corresponding to yours is this: package Base is type Base_Type is null record; procedure Something (X : in out Base_Type); end Base; package Derived is type Derived_Type is new Base_Type; procedure Something (X : in out Derived_Type) is abstract; end Derived; X : Base_Type; Y : Derived_Type; begin Something (X); Something (Y); -- This won't compile > You're right, they are not proper constructors. I haven't found > anything in Ada yet that gives me that. At best they are > initializers. > >>> I really don't like doing this. I don't like using >>> access types for one. It's also doesn't feel like >>> a very clean way because you have to do something >>> out of the ordinary just to use the class like it was >>> meant to be. >> >> Aggregation + delegation is a decent thing. >> >> If you used anonymous access you would reduce dangers of pointers to zero. >> > Definitely. I am also worried about someone copying the access > value and holding onto it past the lifetime of the object. Anonymous access types are difficult to copy. Accessibility rules were made to prevent any unsafe copying. So you will have to break them by doing X.all'Unchecked_Access. > Temp : Base := Derived.Get_Base; -- didn't really want to allow This is illegal. It must be either Copy : Base := Derived.Get_Base.all; or Reference : Base renames Derived.Get_Base.all; Both are safe, yet have a very different semantics. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 21:07 ` Dmitry A. Kazakov @ 2017-05-21 22:28 ` Jere 2017-05-22 8:52 ` Dmitry A. Kazakov 2017-05-22 13:43 ` AdaMagica 1 sibling, 1 reply; 42+ messages in thread From: Jere @ 2017-05-21 22:28 UTC (permalink / raw) On Sunday, May 21, 2017 at 5:07:13 PM UTC-4, Dmitry A. Kazakov wrote: > On 2017-05-21 22:06, Jere wrote: > > On Sunday, May 21, 2017 at 4:44:27 AM UTC-4, Dmitry A. Kazakov wrote: > >> On 2017-05-21 00:51, Jere wrote: > >>> On Saturday, May 20, 2017 at 4:32:07 PM UTC-4, Dmitry A. Kazakov wrote: > >>>> On 2017-05-20 19:33, Jere wrote: > >>> I do know that C++ supports hiding of functions by making them private > >>> in the extending class. > >> > >> No. That is not possible in C++ either. A public method cannot be made > >> private. > >> > >> In general from the OO point of view, a method cannot be removed because > >> it is a property of the class and not of an individual type. It is the > >> contract of the class to have a method Foo. All instances of the class > >> must have this method. If they don't they are not instances of. Period. > >> > >> [To support method disallowing the language must support ad-hoc > >> superclasses, so that one could split an original class into parts to > >> chip the undesired operation away from one part and put the new type in > >> that part instead of the whole original class.] > >> > > The language supports making them private in the context of the > > extending classes. Take for example: > > > > class Base{ > > public: > > class Base_Param{}; > > > > void Something(Base_Param Obj){} > > Something is not a method here. If you make it a method [= Ada's > primitive operation]: > > virtual void Something (Base_Param Obj); > > then your code will stop compiling. Sort of. If you change the code to: class Base{ public: class Base_Param{}; virtual void Something(Base_Param Obj){} }; class Derived : public Base { private: void Something(Base_Param Obj){ /*Do something Safe here*/ } }; class MoreDerived : public Derived{}; and then void run_test(){ Base::Base_Param b; MoreDerived m; m.Something(b); } It will correctly fail as Something() is private in the MoreDerived context. (ERROR= 'virtual void Derived::Something(Base::Base_Param)' is private). However, void run_test(){ Base::Base_Param b; MoreDerived m; Base &r = m; Base *ptr = &m; //Dispatching works correctly r.Something(b); ptr->Something(b); } will happily compile. When you privatize a virtual method, it needs a body, but it will still be private when used as a Derived and MoreDerived type. You are right that in Ada terms, Something is not a primitive operation. C++ doesn't have this requirement though (I'm guessing they are not equivalent to primitive operations because of this), so a non virtual method is still considered a method (in c++) and can be overriden, it just can't dispatch (only virtual functions dispatch in C++). Sorry if I sound combative, I don't mean to be. I think I just am used to different definitions of some terms. Either way, I get that there is no way to do it in Ada. I really do appreciate the responses. > An Ada example corresponding to yours is this: > > package Base is > type Base_Type is null record; > procedure Something (X : in out Base_Type); > end Base; > > package Derived is > type Derived_Type is new Base_Type; > procedure Something (X : in out Derived_Type) is abstract; > end Derived; > > X : Base_Type; > Y : Derived_Type; > begin > Something (X); > Something (Y); -- This won't compile > Yes, I agree, but I don't have control over the base type, so I am stuck with a tagged type. > > You're right, they are not proper constructors. I haven't found > > anything in Ada yet that gives me that. At best they are > > initializers. > > > >>> I really don't like doing this. I don't like using > >>> access types for one. It's also doesn't feel like > >>> a very clean way because you have to do something > >>> out of the ordinary just to use the class like it was > >>> meant to be. > >> > >> Aggregation + delegation is a decent thing. > >> > >> If you used anonymous access you would reduce dangers of pointers to zero. > >> > > Definitely. I am also worried about someone copying the access > > value and holding onto it past the lifetime of the object. > > Anonymous access types are difficult to copy. Accessibility rules were > made to prevent any unsafe copying. So you will have to break them by > doing X.all'Unchecked_Access. > If Get_Access is returning not null access Base.Base_Type, what prevents someone from doing: type My_Base_Access is access all Base.Base_Type; Ptr : My_Base_Access; and then Ptr := Some_Variable.Get_Access; I know you said anonymous access types are difficult to copy, but the small example I tried in GNAT GPL 2016 allows such a copy. Is this a compiler bug? If not, I think I misunderstood. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 22:28 ` Jere @ 2017-05-22 8:52 ` Dmitry A. Kazakov 2017-05-22 13:33 ` AdaMagica 0 siblings, 1 reply; 42+ messages in thread From: Dmitry A. Kazakov @ 2017-05-22 8:52 UTC (permalink / raw) On 22/05/2017 00:28, Jere wrote: > If Get_Access is returning not null access Base.Base_Type, what > prevents someone from doing: > > type My_Base_Access is access all Base.Base_Type; > > Ptr : My_Base_Access; > > and then > > Ptr := Some_Variable.Get_Access; > > I know you said anonymous access types are difficult to copy, > but the small example I tried in GNAT GPL 2016 allows such > a copy. Is this a compiler bug? If not, I think I misunderstood. You can always defeat accessibility checks this or other way. Why should anybody declaring an extra access type? You could also create an instance of Unchecked_Deallocation on My_Base_Access and then free a stack allocated object... -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-22 8:52 ` Dmitry A. Kazakov @ 2017-05-22 13:33 ` AdaMagica 0 siblings, 0 replies; 42+ messages in thread From: AdaMagica @ 2017-05-22 13:33 UTC (permalink / raw) Am Montag, 22. Mai 2017 10:52:42 UTC+2 schrieb Dmitry A. Kazakov: > > You could also create an > instance of Unchecked_Deallocation on My_Base_Access and then free a > stack allocated object... This is one of many ways to make your program erroneous. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 21:07 ` Dmitry A. Kazakov 2017-05-21 22:28 ` Jere @ 2017-05-22 13:43 ` AdaMagica 1 sibling, 0 replies; 42+ messages in thread From: AdaMagica @ 2017-05-22 13:43 UTC (permalink / raw) Am Sonntag, 21. Mai 2017 23:07:13 UTC+2 schrieb Dmitry A. Kazakov: > In Ada a non-primitive operation could be disallowed by declaring it > abstract, just the same. > > An Ada example corresponding to yours is this: > > package Base is > type Base_Type is null record; > procedure Something (X : in out Base_Type); This is a primitive operation of Base_Type. > end Base; > > package Derived is > type Derived_Type is new Base_Type; Because of being primitive, it's inherited here - and you can override it. > procedure Something (X : in out Derived_Type) is abstract; The difference is that this follows the Ada83 derivation rules, which are quite different from the Ada95 rules of tagged types. Nevertheless, operations like Something are called "primitive operations". Also the optional overriding indicators can be used on Ada83 derivation. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 20:06 ` Jere 2017-05-21 21:07 ` Dmitry A. Kazakov @ 2017-05-22 21:17 ` Randy Brukardt 2017-05-25 4:06 ` Jere 1 sibling, 1 reply; 42+ messages in thread From: Randy Brukardt @ 2017-05-22 21:17 UTC (permalink / raw) "Jere" <jhb.chat@gmail.com> wrote in message news:9cdf04e6-123e-4bd9-b466-77aad00d61bb@googlegroups.com... ... > Definitely. I am also worried about someone copying the access > value and holding onto it past the lifetime of the object. I try > to avoid Access types when I can. I wish there was someway to > actually pass a limited type as a function return that didn't > involve build in place that can initialize. I just want to be > able to return the limited object out temporarily to access it: That's what generalized references are for. See 4.1.5 in the RM. Randy. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-22 21:17 ` Randy Brukardt @ 2017-05-25 4:06 ` Jere 2017-05-25 19:39 ` Randy Brukardt 0 siblings, 1 reply; 42+ messages in thread From: Jere @ 2017-05-25 4:06 UTC (permalink / raw) On Monday, May 22, 2017 at 5:17:47 PM UTC-4, Randy Brukardt wrote: > "Jere" wrote in message > ... > > Definitely. I am also worried about someone copying the access > > value and holding onto it past the lifetime of the object. I try > > to avoid Access types when I can. I wish there was someway to > > actually pass a limited type as a function return that didn't > > involve build in place that can initialize. I just want to be > > able to return the limited object out temporarily to access it: > > That's what generalized references are for. See 4.1.5 in the RM. > > Randy. That's a good point. I did end up playing with this and getting an example working. After going through Jeffrey's example, I ended up scrapping the access types altogether, but I do need to learn to leverage this more in cases where access types are necessary. Side question: If I have a type (Gnoga'ish example): type View_Base_Type is new Element_Type with private; --taggeed type View_Type is new View_Base_Type with private; procedure Create (View : in out View_Type; Parent : in out Base_Type'Class; ID : in String := ""); -- Excuse any typos, this is hand typed code And then I extend it: type My_Type is new View_Base_Type with private; procedure Create (Obj : in out My_Type; Parent : in out Window_Type'Class; -- derived from Base_Type'Class ID : in String := ""); private type My_Type is new View_Type with null record; Sometimes if I subtype or extend My_Type, I get the ambiguous calls to Create issue due to the inherited Create function from View_Type. Since the extension of View_Type was private (I publicly extended View_Base_Type), should there still be an ambiguity between the Create procedures? My new one shouldn't be an override and since the extension to View_Type is private, it shouldn't be callable (I'm not in a child package, this would be just some other unrelated package subtyping or extending My_Type). I get that before my extension was public so making the Create procedure private wasn't an option, but I had hoped with the full view being private it wouldn't cause the ambiguity. I realize in code that could view the private section the issue might still be there, but this wouldn't be a package that can see the private section. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-25 4:06 ` Jere @ 2017-05-25 19:39 ` Randy Brukardt 2017-05-25 22:53 ` Jere 0 siblings, 1 reply; 42+ messages in thread From: Randy Brukardt @ 2017-05-25 19:39 UTC (permalink / raw) Your probably having trouble with the appropriaate view of My_Type. The full type of My_Type has both versions of Create (of course), as it inherits from View_Type. If you try to extend somewhere where that full view is visible, then that extension too will get both Creates. (That includes in the body and in any child packages.) OTOH, an extension somewhere that only has visibility on the private type, you should only get one (visible) Create. (But the other one is still inherited, it just can't be called directly.) I could go into more detail if you need that; it would help to have a full compilable example in that case so I can see where everything is declared. Randy. "Jere" <jhb.chat@gmail.com> wrote in message news:e9d5050e-1046-4f8a-92e0-feded33f7a50@googlegroups.com... > On Monday, May 22, 2017 at 5:17:47 PM UTC-4, Randy Brukardt wrote: >> "Jere" wrote in message >> ... >> > Definitely. I am also worried about someone copying the access >> > value and holding onto it past the lifetime of the object. I try >> > to avoid Access types when I can. I wish there was someway to >> > actually pass a limited type as a function return that didn't >> > involve build in place that can initialize. I just want to be >> > able to return the limited object out temporarily to access it: >> >> That's what generalized references are for. See 4.1.5 in the RM. >> >> Randy. > > That's a good point. I did end up playing with this and getting > an example working. After going through Jeffrey's example, I > ended up scrapping the access types altogether, but I do need > to learn to leverage this more in cases where access types > are necessary. > > Side question: > > If I have a type (Gnoga'ish example): > > type View_Base_Type is new Element_Type with private; --taggeed > type View_Type is new View_Base_Type with private; > procedure Create > (View : in out View_Type; > Parent : in out Base_Type'Class; > ID : in String := ""); > > -- Excuse any typos, this is hand typed code > > And then I extend it: > > type My_Type is new View_Base_Type with private; > procedure Create > (Obj : in out My_Type; > Parent : in out Window_Type'Class; -- derived from Base_Type'Class > ID : in String := ""); > > private > > type My_Type is new View_Type with null record; > > Sometimes if I subtype or extend My_Type, I get the ambiguous > calls to Create issue due to the inherited Create function > from View_Type. > > Since the extension of View_Type was private (I publicly > extended View_Base_Type), should there still be an > ambiguity between the Create procedures? My new > one shouldn't be an override and since the extension > to View_Type is private, it shouldn't be callable > (I'm not in a child package, this would be just > some other unrelated package subtyping or extending > My_Type). I get that before my extension was public > so making the Create procedure private wasn't an > option, but I had hoped with the full view being > private it wouldn't cause the ambiguity. I realize > in code that could view the private section the issue > might still be there, but this wouldn't be a package > that can see the private section. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-25 19:39 ` Randy Brukardt @ 2017-05-25 22:53 ` Jere 2017-05-25 22:57 ` Jere 2017-05-26 20:46 ` Randy Brukardt 0 siblings, 2 replies; 42+ messages in thread From: Jere @ 2017-05-25 22:53 UTC (permalink / raw) On Thursday, May 25, 2017 at 3:40:01 PM UTC-4, Randy Brukardt wrote: > Your probably having trouble with the appropriaate view of My_Type. The full > type of My_Type has both versions of Create (of course), as it inherits from > View_Type. If you try to extend somewhere where that full view is visible, > then that extension too will get both Creates. (That includes in the body > and in any child packages.) > > OTOH, an extension somewhere that only has visibility on the private type, > you should only get one (visible) Create. (But the other one is still > inherited, it just can't be called directly.) > > I could go into more detail if you need that; it would help to have a full > compilable example in that case so I can see where everything is declared. > > Randy. > > This might be a better example. I have 3 tiers: Base/Derived/More_Derived More_Derived publicly inherits off of Base but privately inherits off of Derived. Derived provides a Something procedure and More_Derived provides a Something procedure but uses a different signature and is not overriding (I double checked by putting "overriding" with it and noted the compiler error). More_Derived does not have private view of Derived, but GNAT still says there is a ambiguity. I don't see how More_Derived has a private view of Derived, so I think this is a bug, but I might be missing something. main.adb ******************************************* with Base; with Derived; with More_Derived; procedure Main is p : Base.Derived_Param; subtype My_Type is More_Derived.More_Derived_Type; m : My_Type; begin m.Something(p); -- ERROR: ambiguous call to Something end Main; ******************************************* base.ads ************************************************* package Base is type Base_Param is tagged null record; type Derived_Param is new Base.Base_Param with null record; type Base_Type is tagged null record; end Base; ************************************************* derived.ads ************************************************* with Base; package Derived is type Derived_Type is new Base.Base_Type with null record; procedure Something (Obj : in out Derived_Type; Value : Base.Base_Param'Class) is null; end Derived; ************************************************* more_derived.ads ************************************************* with Base; with Derived; package More_Derived is type More_Derived_Type is new Base.Base_Type with private; procedure Something (Obj : in out More_Derived_Type; Value : Base.Derived_Param'Class) is null; private type More_Derived_Type is new Derived.Derived_Type with null record; end More_Derived; ************************************************* If I don't use the subtype in main.adb, it compiles fine. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-25 22:53 ` Jere @ 2017-05-25 22:57 ` Jere 2017-05-26 20:46 ` Randy Brukardt 1 sibling, 0 replies; 42+ messages in thread From: Jere @ 2017-05-25 22:57 UTC (permalink / raw) On Thursday, May 25, 2017 at 6:53:10 PM UTC-4, Jere wrote: > On Thursday, May 25, 2017 at 3:40:01 PM UTC-4, Randy Brukardt wrote: > > Your probably having trouble with the appropriaate view of My_Type. The full > > type of My_Type has both versions of Create (of course), as it inherits from > > View_Type. If you try to extend somewhere where that full view is visible, > > then that extension too will get both Creates. (That includes in the body > > and in any child packages.) > > > > OTOH, an extension somewhere that only has visibility on the private type, > > you should only get one (visible) Create. (But the other one is still > > inherited, it just can't be called directly.) > > > > I could go into more detail if you need that; it would help to have a full > > compilable example in that case so I can see where everything is declared. > > > > Randy. > > > > > > This might be a better example. I have 3 tiers: Base/Derived/More_Derived > > More_Derived publicly inherits off of Base but privately inherits off of > Derived. > > Derived provides a Something procedure and More_Derived provides a Something > procedure but uses a different signature and is not overriding (I double > checked by putting "overriding" with it and noted the compiler error). > > More_Derived does not have private view of Derived, but GNAT still says > there is a ambiguity. I don't see how More_Derived has a private > view of Derived, so I think this is a bug, but I might be missing > something. > > [snipped] > > If I don't use the subtype in main.adb, it compiles fine. Forgot to mention it is GNAT GPL 2016. I don't have FSF working on my Win10 machine yet to check. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-25 22:53 ` Jere 2017-05-25 22:57 ` Jere @ 2017-05-26 20:46 ` Randy Brukardt 2017-05-26 22:35 ` Simon Wright 2017-05-26 22:58 ` Jeffrey R. Carter 1 sibling, 2 replies; 42+ messages in thread From: Randy Brukardt @ 2017-05-26 20:46 UTC (permalink / raw) Ah, a subtype. GNAT has (or had?)a bug having to do with making things visible because of a subtype declaration (I recall someone else running across that and having verified it myself). That's just plain wrong, a subtype has no effect on what's visible in a prefixed call (or anywhere else for that matter, in particular with "use all type"). You ought to have an ambiguity if you tried to call Something in the body of More_Derived, but it should be OK in the main subprogram. I'd skip the subtype and just use the real name of the type. And possibly make a bug report to AdaCore. You could also use a traditional call (More_Derived.Something (M, P);) which would avoid the buggy part of GNAT. Randy. "Jere" <jhb.chat@gmail.com> wrote in message news:62e326e6-dd15-4546-83dc-b1e423327c23@googlegroups.com... > On Thursday, May 25, 2017 at 3:40:01 PM UTC-4, Randy Brukardt wrote: >> Your probably having trouble with the appropriaate view of My_Type. The >> full >> type of My_Type has both versions of Create (of course), as it inherits >> from >> View_Type. If you try to extend somewhere where that full view is >> visible, >> then that extension too will get both Creates. (That includes in the body >> and in any child packages.) >> >> OTOH, an extension somewhere that only has visibility on the private >> type, >> you should only get one (visible) Create. (But the other one is still >> inherited, it just can't be called directly.) >> >> I could go into more detail if you need that; it would help to have a >> full >> compilable example in that case so I can see where everything is >> declared. >> >> Randy. >> >> > > This might be a better example. I have 3 tiers: > Base/Derived/More_Derived > > More_Derived publicly inherits off of Base but privately inherits off of > Derived. > > Derived provides a Something procedure and More_Derived provides a > Something > procedure but uses a different signature and is not overriding (I double > checked by putting "overriding" with it and noted the compiler error). > > More_Derived does not have private view of Derived, but GNAT still says > there is a ambiguity. I don't see how More_Derived has a private > view of Derived, so I think this is a bug, but I might be missing > something. > > main.adb > ******************************************* > with Base; > with Derived; > with More_Derived; > > procedure Main is > p : Base.Derived_Param; > > subtype My_Type is More_Derived.More_Derived_Type; > > m : My_Type; > begin > m.Something(p); -- ERROR: ambiguous call to Something > end Main; > ******************************************* > > > base.ads > ************************************************* > package Base is > > type Base_Param is tagged null record; > type Derived_Param is new Base.Base_Param with null record; > > type Base_Type is tagged null record; > > end Base; > ************************************************* > > > derived.ads > ************************************************* > with Base; > > package Derived is > > type Derived_Type is new Base.Base_Type with null record; > > procedure Something > (Obj : in out Derived_Type; > Value : Base.Base_Param'Class) > is null; > > end Derived; > ************************************************* > > > more_derived.ads > ************************************************* > with Base; > with Derived; > > package More_Derived is > > type More_Derived_Type is new Base.Base_Type with private; > > procedure Something > (Obj : in out More_Derived_Type; > Value : Base.Derived_Param'Class) > is null; > > private > > type More_Derived_Type is new Derived.Derived_Type with null record; > > end More_Derived; > ************************************************* > > If I don't use the subtype in main.adb, it compiles fine. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-26 20:46 ` Randy Brukardt @ 2017-05-26 22:35 ` Simon Wright 2018-05-20 11:22 ` Simon Wright 2017-05-26 22:58 ` Jeffrey R. Carter 1 sibling, 1 reply; 42+ messages in thread From: Simon Wright @ 2017-05-26 22:35 UTC (permalink / raw) "Randy Brukardt" <randy@rrsoftware.com> writes: > Ah, a subtype. GNAT has (or had?)a bug having to do with making things > visible because of a subtype declaration (I recall someone else > running across that and having verified it myself). That's just plain > wrong, a subtype has no effect on what's visible in a prefixed call > (or anywhere else for that matter, in particular with "use all type"). > > You ought to have an ambiguity if you tried to call Something in the > body of More_Derived, but it should be OK in the main subprogram. > > I'd skip the subtype and just use the real name of the type. And > possibly make a bug report to AdaCore. > You could also use a traditional call (More_Derived.Something (M, P);) > which would avoid the buggy part of GNAT. Quite right, Randy (GCC 7.1.0, still; has anyone reported this?) ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-26 22:35 ` Simon Wright @ 2018-05-20 11:22 ` Simon Wright 2018-05-20 12:03 ` Jere 0 siblings, 1 reply; 42+ messages in thread From: Simon Wright @ 2018-05-20 11:22 UTC (permalink / raw) Simon Wright <simon@pushface.org> writes: > "Randy Brukardt" <randy@rrsoftware.com> writes: > >> Ah, a subtype. GNAT has (or had?)a bug having to do with making things >> visible because of a subtype declaration (I recall someone else >> running across that and having verified it myself). That's just plain >> wrong, a subtype has no effect on what's visible in a prefixed call >> (or anywhere else for that matter, in particular with "use all type"). >> >> You ought to have an ambiguity if you tried to call Something in the >> body of More_Derived, but it should be OK in the main subprogram. >> >> I'd skip the subtype and just use the real name of the type. And >> possibly make a bug report to AdaCore. >> You could also use a traditional call (More_Derived.Something (M, P);) >> which would avoid the buggy part of GNAT. > > Quite right, Randy (GCC 7.1.0, still; has anyone reported this?) I think it must have been reported, because it's fixed in 8.1.0. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2018-05-20 11:22 ` Simon Wright @ 2018-05-20 12:03 ` Jere 0 siblings, 0 replies; 42+ messages in thread From: Jere @ 2018-05-20 12:03 UTC (permalink / raw) On Sunday, May 20, 2018 at 7:22:09 AM UTC-4, Simon Wright wrote: > Simon Wright writes: > > > "Randy Brukardt" writes: > > > >> Ah, a subtype. GNAT has (or had?)a bug having to do with making things > >> visible because of a subtype declaration (I recall someone else > >> running across that and having verified it myself). That's just plain > >> wrong, a subtype has no effect on what's visible in a prefixed call > >> (or anywhere else for that matter, in particular with "use all type"). > >> > >> You ought to have an ambiguity if you tried to call Something in the > >> body of More_Derived, but it should be OK in the main subprogram. > >> > >> I'd skip the subtype and just use the real name of the type. And > >> possibly make a bug report to AdaCore. > >> You could also use a traditional call (More_Derived.Something (M, P);) > >> which would avoid the buggy part of GNAT. > > > > Quite right, Randy (GCC 7.1.0, still; has anyone reported this?) > > I think it must have been reported, because it's fixed in 8.1.0. I ended up reporting this to them sometime shortly after. I'm glad the fix finally made it in! ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-26 20:46 ` Randy Brukardt 2017-05-26 22:35 ` Simon Wright @ 2017-05-26 22:58 ` Jeffrey R. Carter 2017-05-30 21:15 ` Randy Brukardt 1 sibling, 1 reply; 42+ messages in thread From: Jeffrey R. Carter @ 2017-05-26 22:58 UTC (permalink / raw) On 05/26/2017 10:46 PM, Randy Brukardt wrote: > Ah, a subtype. GNAT has (or had?)a bug having to do with making things > visible because of a subtype declaration (I recall someone else running > across that and having verified it myself). That's just plain wrong, a > subtype has no effect on what's visible in a prefixed call (or anywhere else > for that matter, in particular with "use all type"). That was me. I wasn't aware that you had verified it as a compiler error. -- Jeff Carter "Nobody expects the Spanish Inquisition!" Monty Python's Flying Circus 22 ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-26 22:58 ` Jeffrey R. Carter @ 2017-05-30 21:15 ` Randy Brukardt 2017-06-02 1:07 ` Jere 0 siblings, 1 reply; 42+ messages in thread From: Randy Brukardt @ 2017-05-30 21:15 UTC (permalink / raw) "Jeffrey R. Carter" <spam.jrcarter.not@spam.not.acm.org> wrote in message news:ogabnv$9hj$1@dont-email.me... > On 05/26/2017 10:46 PM, Randy Brukardt wrote: >> Ah, a subtype. GNAT has (or had?)a bug having to do with making things >> visible because of a subtype declaration (I recall someone else running >> across that and having verified it myself). That's just plain wrong, a >> subtype has no effect on what's visible in a prefixed call (or anywhere >> else >> for that matter, in particular with "use all type"). > > That was me. I wasn't aware that you had verified it as a compiler error. I don't recall the details anymore, but I'm pretty sure I discussed that with someone at AdaCore. (Either in the context of an ACATS test, or just on the side of some other conversation.) And they agreed it was wrong. No idea if it formally got reported though. Randy. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-30 21:15 ` Randy Brukardt @ 2017-06-02 1:07 ` Jere 2017-06-02 7:31 ` Dmitry A. Kazakov ` (2 more replies) 0 siblings, 3 replies; 42+ messages in thread From: Jere @ 2017-06-02 1:07 UTC (permalink / raw) On Tuesday, May 30, 2017 at 5:15:20 PM UTC-4, Randy Brukardt wrote: > "Jeffrey R. Carter" wrote in message > > On 05/26/2017 10:46 PM, Randy Brukardt wrote: > >> Ah, a subtype. GNAT has (or had?)a bug having to do with making things > >> visible because of a subtype declaration (I recall someone else running > >> across that and having verified it myself). That's just plain wrong, a > >> subtype has no effect on what's visible in a prefixed call (or anywhere > >> else > >> for that matter, in particular with "use all type"). > > > > That was me. I wasn't aware that you had verified it as a compiler error. > > I don't recall the details anymore, but I'm pretty sure I discussed that > with someone at AdaCore. (Either in the context of an ACATS test, or just on > the side of some other conversation.) And they agreed it was wrong. No idea > if it formally got reported though. > > Randy. Thanks for the verification on that (and thanks to Simon for checking a more recent version). I went to the Adacore website but it looks like all of their bug submission options require you to be a paying customer. I'll have to figure out a way to report this to Adacore (or is there a different route?). What are your thoughts on this suggestion: Generally, constructing operations don't need to be primitive. In some other languages, constructors are not primitive at all and can neither dispatch nor be overridden. It would be nice if Ada provided a more straightforward way to set a procedure or function as not primitive. I know you can put them in another package (or a subpackage), but that leads to really clumsy naming schemes for some packages or unnecessary extra files. I don't know enough to say what the right way to do it would be. I feel an extra keyword is overkill, but I don't know if either aspects or attributes would be a viable alternative ( with Primitive => False or for My_Function'Primitive use False). It wouldn't need to cause freezing, but just prevent extensions from inheriting or dispatching to them. If I am using primitive incorrectly, I apologize. Hopefully my meaning is at least decipherable. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-06-02 1:07 ` Jere @ 2017-06-02 7:31 ` Dmitry A. Kazakov 2017-06-02 8:09 ` Mark Lorenzen 2017-06-02 11:31 ` Simon Wright 2 siblings, 0 replies; 42+ messages in thread From: Dmitry A. Kazakov @ 2017-06-02 7:31 UTC (permalink / raw) On 02/06/2017 03:07, Jere wrote: > It would be nice if Ada provided a more > straightforward way to set a procedure or function as not primitive. I would argue that from theoretical point of view there should be no free operations at all. E.g. all operations must be primitive, dispatching, all arguments and results always controlled. [*]. > I know you can put them in another package (or a subpackage), ... or if you freeze the type by using it (in a non-trivial manner) before declaring the operation. ------------ * A class-wide operation procedure Foo (X : T'Class) would be dispatching on T'Class'Class should you have such object. And in general for each type T there is T'Class. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-06-02 1:07 ` Jere 2017-06-02 7:31 ` Dmitry A. Kazakov @ 2017-06-02 8:09 ` Mark Lorenzen 2017-06-02 11:31 ` Simon Wright 2 siblings, 0 replies; 42+ messages in thread From: Mark Lorenzen @ 2017-06-02 8:09 UTC (permalink / raw) On Friday, June 2, 2017 at 3:07:06 AM UTC+2, Jere wrote: > > Thanks for the verification on that (and thanks to Simon for > checking a more recent version). I went to the Adacore website > but it looks like all of their bug submission options require > you to be a paying customer. I'll have to figure out a way to > report this to Adacore (or is there a different route?). I think if you are using GNAT GPL you can submit a bug report here: http://libre.adacore.com/contact/ Regards, Mark L ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-06-02 1:07 ` Jere 2017-06-02 7:31 ` Dmitry A. Kazakov 2017-06-02 8:09 ` Mark Lorenzen @ 2017-06-02 11:31 ` Simon Wright 2 siblings, 0 replies; 42+ messages in thread From: Simon Wright @ 2017-06-02 11:31 UTC (permalink / raw) Jere <jhb.chat@gmail.com> writes: > I went to the Adacore website but it looks like all of their bug > submission options require you to be a paying customer. I'll have to > figure out a way to report this to Adacore (or is there a different > route?). Email report@adacore.com with GNAT: at the start of your Subject line. AdaCore may or may not respond. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-20 20:32 ` Dmitry A. Kazakov 2017-05-20 22:51 ` Jere @ 2017-05-22 21:12 ` Randy Brukardt 2017-05-23 7:38 ` Dmitry A. Kazakov 1 sibling, 1 reply; 42+ messages in thread From: Randy Brukardt @ 2017-05-22 21:12 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:ofq943$14v4$1@gioia.aioe.org... ... > ---------------------------------- > The actual problem you have is parallel types hierarchies. You want to > derive tied instances of Base_Type'Class and Base_Param'Class. > > Base_Type ----- Base_Param > | | > Derived_Type -- Derived_Param > > This requires > > 1. Full multiple dispatch > 2. Dispatch constrained to certain combinations (parallel hierarchies) > > This is not supported in Ada (or in any other OO language I am aware of) Right. Having investigated this, it seems impossible to support in any language that is intended to support LSP (which is the backbone of OOP). The basic problem is dispatching. In Ada terms, you have a call: Something (Classwide_Obj, Othertype_Obj); where Classwide_Obj is of Root'Class. Now, the problem is that the other (usually untagged) parameter is of the wrong type for the routines that you dispatch to. There are various ways you can fix this dynamically (for instance, as you noted, with multiple dispatch), but there is no way to have any static typing in these cases. But the entire point of doing a "co-derivation" is to get static typing, so you're doing a lot of work for very little gain. Co-derivation probably could be made to work for untagged types (as they don't have dispatching to worry about), but it's unclear that enough benefit would arise. Anyway, this is an agenda item for the ARG, but unless someone has an idea that hasn't been considered to date it isn't going anywhere. Randy. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-22 21:12 ` Randy Brukardt @ 2017-05-23 7:38 ` Dmitry A. Kazakov 0 siblings, 0 replies; 42+ messages in thread From: Dmitry A. Kazakov @ 2017-05-23 7:38 UTC (permalink / raw) On 22/05/2017 23:12, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:ofq943$14v4$1@gioia.aioe.org... > ... >> ---------------------------------- >> The actual problem you have is parallel types hierarchies. You want to >> derive tied instances of Base_Type'Class and Base_Param'Class. >> >> Base_Type ----- Base_Param >> | | >> Derived_Type -- Derived_Param >> >> This requires >> >> 1. Full multiple dispatch >> 2. Dispatch constrained to certain combinations (parallel hierarchies) >> >> This is not supported in Ada (or in any other OO language I am aware of) > > Right. Having investigated this, it seems impossible to support in any > language that is intended to support LSP (which is the backbone of OOP). The > basic problem is dispatching. In Ada terms, you have a call: > > Something (Classwide_Obj, Othertype_Obj); > > where Classwide_Obj is of Root'Class. Now, the problem is that the other > (usually untagged) parameter is of the wrong type for the routines that you > dispatch to. A rough idea would be to have classes of tuples. The tag is then assigned to the tuple of types: (Base_Type, Base_Param) rather than to an individual type. The call X : Base_Type'Class := Y : Base_Param := Something (X, Y); would be statically illegal because (X, Y) is not in the class (Base_Type x Base_Param)'Class but in Base_Type'Class x Base_Type Also illegal would be X : Derived_Type := Y : Base_Param := Something (X, Y); Because (Derived_Type, Base_Param) is not in the class either. One should invent something to flatten tuples and means to produce class-wide tuples implicitly and explicitly. This model is fully statically checkable and supports dispatch. > There are various ways you can fix this dynamically (for instance, as you > noted, with multiple dispatch), but there is no way to have any static > typing in these cases. Right, but that is rather uninteresting from Ada's and SPARK POV. > But the entire point of doing a "co-derivation" is to > get static typing, so you're doing a lot of work for very little gain. Exactly. > Co-derivation probably could be made to work for untagged types (as they > don't have dispatching to worry about), but it's unclear that enough benefit > would arise. Right, but that works exactly because there is no class-wide objects of untagged types. > Anyway, this is an agenda item for the ARG, but unless someone has an idea > that hasn't been considered to date it isn't going anywhere. Very good. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-20 17:33 Preventing private procedure visibility being made public through extension Jere 2017-05-20 20:13 ` AdaMagica 2017-05-20 20:32 ` Dmitry A. Kazakov @ 2017-05-21 18:14 ` Robert Eachus 2017-05-21 20:21 ` Jere 2 siblings, 1 reply; 42+ messages in thread From: Robert Eachus @ 2017-05-21 18:14 UTC (permalink / raw) On Saturday, May 20, 2017 at 1:33:27 PM UTC-4, Jere wrote: > I tried to whittle this down to a small example, so forgive > the uselessness of the example itself. I'm extending a > library with some of my own components and I want (if possible) > to allow my components to interact with the other library > components as seamlessly as possible. > > Say a library provides a tagged type with the following procedure: > > Base.ads > ************************************************* > package Base is > > type Base_Param is tagged null record; > > type Base_Type is tagged null record; > > procedure Something > (Obj : in out Base_Type; > Value : Base_Param'Class) > is null; > > end Base; > > ************************************************* ... I think that the problem you are dealing with is the you started with one package and have never let go of that model. You have two very different access patterns in mind (not access in the Ada sense). So you need two packages to declare them. Is there any reason to allow your clients to do anything with Base_Param other than pass it to subroutines you provide? I think not. I’m also going to avoid games like the Taft amendment, Use it if you want to. generic type Object is limited private; package Params is begin type Param is limited private; private type Access_Object is access Object; type Param is record Prev, Next: Access_Object; end record; end Params; with Params; package Base is type Base_Type is tagged null record; type Base_Access is limited private; procedure Something (Obj : in out Base_Type; Value : Base_Access); private type Base_Access is access Base_Type; package My_Params is new Params(Base_Type); end Base; package body Base –- declare any necessary operations of Base_Access here that way you only need -- one body procedure Something (Obj : in out Base_Type; Value : Base_Access) is begin null; end Something; end Base; I’ve used a mindset of you are going to be maintaining lists, databases or whatever of Base_Type, and would want Base_Type to be derived from Controlled or Limited_Controlled. If you do need both Base_Type to be tagged, fine. You will find that the two package model works fine if the classes, as such, are disjoint. But in Ada I have found that two tagged types in the same package eventually lead to madness. If you have M (say 3) flavors of one type, and N (say 4) of the other, now you have M times N (twelve_ sets of derived operations that can exist. Adding another derivation will always break something. Having the types declared in separate packages allows you to stay sane while trying to maintain the rats nest. I’ve done the M plus N style with window managers where a dozen deep in one dimension (window manager) and six in another (program logic) was version 1.0... ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 18:14 ` Robert Eachus @ 2017-05-21 20:21 ` Jere 2017-05-21 21:09 ` Jeffrey R. Carter 2017-05-21 21:20 ` Dmitry A. Kazakov 0 siblings, 2 replies; 42+ messages in thread From: Jere @ 2017-05-21 20:21 UTC (permalink / raw) On Sunday, May 21, 2017 at 2:14:23 PM UTC-4, Robert Eachus wrote: > On Saturday, May 20, 2017 at 1:33:27 PM UTC-4, Jere wrote: > > I tried to whittle this down to a small example, so forgive > > the uselessness of the example itself. I'm extending a > > library with some of my own components and I want (if possible) > > to allow my components to interact with the other library > > components as seamlessly as possible. > > > > Say a library provides a tagged type with the following procedure: > > > > Base.ads > > ************************************************* > > package Base is > > > > type Base_Param is tagged null record; > > > > type Base_Type is tagged null record; > > > > procedure Something > > (Obj : in out Base_Type; > > Value : Base_Param'Class) > > is null; > > > > end Base; > > > > ************************************************* > ... > I think that the problem you are dealing with is the you > started with one package and have never let go of that > model. You have two very different access patterns in > mind (not access in the Ada sense). So you need two > packages to declare them. Is there any reason to allow > your clients to do anything with Base_Param other than > pass it to subroutines you provide? I think not. I’m > also going to avoid games like the Taft amendment, > Use it if you want to. Sorry, that was just an example. The actual use case has them in separate packages, but the problem is the same: 1. I want my type to be usable in the 3rd party library, so I need to extend a type from it or provide access to an internal parameter of one of its types. 2. I don't want to provide some of the library type's public procedures as they can mess up my derived type if called. Per Dmitry's suggestion, I can provide runtime safety, but I was looking for static safety. At the moment I am leaning using an encapsulating type that passes back an access to an internal component so I can still interface to the library. I just don't like using access types if I can avoid them. So something like: type My_Type is tagged limited private; function Get_Libaray_Component (Obj : in out My_Type) return not null access Library_Type; private type My_Type is new Other_Library_Type with record Base : aliased Library_Type; end record; Then I can call: Library.Some_Library_Function(My_Var.Get_Library_Component.all); But none of Other_Library_Type's procedures can be publicly called on my extended type, breaking it. Note that the library I am interfacing with already exists and is not mine. I don't want to modify it or else any code I augment it with won't be usable to others without them modding their copies as well. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 20:21 ` Jere @ 2017-05-21 21:09 ` Jeffrey R. Carter 2017-05-21 22:46 ` Jere 2017-05-21 21:20 ` Dmitry A. Kazakov 1 sibling, 1 reply; 42+ messages in thread From: Jeffrey R. Carter @ 2017-05-21 21:09 UTC (permalink / raw) On 05/21/2017 10:21 PM, Jere wrote: > > I just don't > like using access types if I can avoid them. Of course you can avoid them. > So something like: > > type My_Type is tagged limited private; > function Get_Libaray_Component > (Obj : in out My_Type) > return not null access Library_Type; > private > type My_Type is new Other_Library_Type with record > Base : aliased Library_Type; > end record; There are a finite number of operations defined for Library_Type, and, as you said, a smaller number that make sense for your abstraction. So you simply define equivalent operations for My_Type that call the appropriate operations for Library_Type with the Base component. No access types, simple, safe, and clear. -- Jeff Carter "I soiled my armor, I was so scared." Monty Python & the Holy Grail 71 ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 21:09 ` Jeffrey R. Carter @ 2017-05-21 22:46 ` Jere 2017-05-22 21:24 ` Jeffrey R. Carter 0 siblings, 1 reply; 42+ messages in thread From: Jere @ 2017-05-21 22:46 UTC (permalink / raw) On Sunday, May 21, 2017 at 5:09:59 PM UTC-4, Jeffrey R. Carter wrote: > On 05/21/2017 10:21 PM, Jere wrote: > > > > I just don't > > like using access types if I can avoid them. > > Of course you can avoid them. > > > So something like: > > > > type My_Type is tagged limited private; > > function Get_Libaray_Component > > (Obj : in out My_Type) > > return not null access Library_Type; > > private > > type My_Type is new Other_Library_Type with record > > Base : aliased Library_Type; > > end record; > > There are a finite number of operations defined for Library_Type, and, as you > said, a smaller number that make sense for your abstraction. So you simply > define equivalent operations for My_Type that call the appropriate operations > for Library_Type with the Base component. No access types, simple, safe, and clear. I'll use a more concrete example here. I created a modal dialog box type for Gnoga. My spec looks like: with Gnoga.Gui.Window; with Gnoga.Gui.View; package Gnoga.Gui.Modal_Dialog is type Dialog_Type is tagged limited private; type Dialog_Access is access all Dialog_Type; type Pointer_To_Dialog_Class is access all Dialog_Type'Class; procedure Create (Dialog : in out Dialog_Type; Parent : in out Gnoga.Gui.Window.Window_Type'Class; ID : in String := ""); private type Dialog_Type is new Gnoga.Gui.View.View_Type with record Main_View : Gnoga.Gui.View.View_Access := null; end record; end Gnoga.Gui.Modal_Dialog; and the body: with Gnoga.Gui.Window; with Gnoga.Gui.Base; with Gnoga.Gui.Element; package body Gnoga.Gui.Modal_Dialog is procedure Create (Dialog : in out Dialog_Type; Parent : in out Gnoga.Gui.Window.Window_Type'Class; ID : in String := "") is use type Gnoga.Gui.View.View_Access; Old_View : Gnoga.Gui.Base.Pointer_To_Base_Class := Parent.Get_View; begin if Dialog.Main_View = null then -- Create the Dialog Gnoga.Gui.View.View_Type(Dialog).Create (Parent => Parent, ID => ID); -- Creating a view using a window as a parent sets the view as the -- window's main view. This line sets it back to the original. Parent.Set_View(Old_View.all); -- Configure the Modal Background Dialog.Fill_Parent; Dialog.Background_Color("Grey"); -- Set the default show/hide state Dialog.Show(False); -- Create the main view of the dialog Dialog.Main_View := new Gnoga.Gui.View.View_Type; Dialog.Main_View.Dynamic; Dialog.Main_View.Create(Dialog); Dialog.Main_View.Background_Color("White"); Dialog.Main_View.Position(Gnoga.Gui.Element.Fixed); -- Center the view as a default Dialog.Center; end if; end Create; end Gnoga.Gui.Modal_Dialog; At this point, I need a clean way to let a client add components (Buttons, forms, etc.) to the dialog box. Adding all the operations to Dialog_Type won't help me since all the components use a Create procedure that takes a Gnoga.Gui.Base.Base_Type'Class parent. I don't want to publicly expose the extension of Dialog_Type since procedures like Create_From_HTML and such will really mess with the Dialog_Type. My initial solution was to add: function Get_View (Dialog : in out Dialog_Type) return Gnoga.Gui.View.View_Access is begin return Dialog.Main_View; end Get_View; which is why you see Main_View declare as an access type internally (it was originally just a Gnoga.Gui.View.View_Type before). I don't like this method and am open to other options. Basically, I need something to pass to the Create procedure for any components added. The other option I was tossing around was to instead add a procedure Add_Component (Dialog : in out Dialog_Type; Object : in out Gnoga.Gui.Base.Basetype'Class); but the problem there is the components still need to call Create on some parent, and for the dialog box to work, I think it needs to be the view in my tagged type. Thoughts? ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 22:46 ` Jere @ 2017-05-22 21:24 ` Jeffrey R. Carter 2017-05-25 3:45 ` Jere 0 siblings, 1 reply; 42+ messages in thread From: Jeffrey R. Carter @ 2017-05-22 21:24 UTC (permalink / raw) On 05/22/2017 12:46 AM, Jere wrote: > > I'll use a more concrete example here. I created a modal dialog box type > for Gnoga. My spec looks like: I gathered from your other posts that you were talking about this. > with Gnoga.Gui.Window; > with Gnoga.Gui.View; > package Gnoga.Gui.Modal_Dialog is > > type Dialog_Type is tagged limited private; > type Dialog_Access is access all Dialog_Type; > type Pointer_To_Dialog_Class is access all Dialog_Type'Class; > > procedure Create > (Dialog : in out Dialog_Type; > Parent : in out Gnoga.Gui.Window.Window_Type'Class; > ID : in String := ""); > > private > > type Dialog_Type is new Gnoga.Gui.View.View_Type with record > Main_View : Gnoga.Gui.View.View_Access := null; > end record; > > end Gnoga.Gui.Modal_Dialog; I don't see any reason for the access type in the full view. It could probably just be View_Type, but I think it should really be Gnoga.Gui.Element.Form.Form_Type, as I'll explain later. > At this point, I need a clean way to let a client > add components (Buttons, forms, etc.) to the dialog box. > > Adding all the operations to Dialog_Type won't help me > since all the components use a Create procedure that > takes a Gnoga.Gui.Base.Base_Type'Class parent. I don't > want to publicly expose the extension of Dialog_Type > since procedures like Create_From_HTML and such > will really mess with the Dialog_Type. Right. You need to be able control what the client can do with the dialog, so using a View_Access and providing it to the client is a Bad Idea. > The other option I was tossing around was to > instead add a > > procedure Add_Component > (Dialog : in out Dialog_Type; > Object : in out Gnoga.Gui.Base.Basetype'Class); Mostly what people need in such a dialog are buttons and text widgets (Text_Type). The former are from Gnoga.Gui.Element.Common and the latter from Gnoga.Gui.Element.Form, which is why I think your inner view should be a form. You can create buttons with a form as the parent, but you can't create a text widget without a form. (This seems wrong to me, but that's how Gnoga is. I've often used form widgets but never needed the form functionality.) So as a first cut you could only have procedure Create_Button (Button : in out Button_Type; Content : in String := ""; ID : in String := ""); procedure Create_Text (Text : in out Text_Type; Size : in Positive := 20; Value : in String := ""; Name : in String := ""; ID : in String := ""; Label : in String := ""); and you'd probably cover 95% of uses. Internally, you'd call Button.Create with your form as Parent, and Text.Create with your form as Form. Your client can then attach an on-click handler to the buttons and call Value for the text widgets. If Label is not null, you would create a label to go with Text with Label as its Content. You might also want to provide a Create_Div that the client could put a bunch of text into, and a New_Line for putting things on different lines. So you could have dialogs like +----------------------------------------+ | You have to authenticate to do that | | __________________________ | | Password: |__________________________| | | | | [ OK ] [ Cancel ] | +----------------------------------------+ Where the 1st line is a Div, the 2nd a Text_Type (or Password_Type; see below) with a label, and the 3rd 2 buttons. There are a bunch of specialized children of Text_Type, all with the same parameter profile for their Create procedures, so you could replace Text_Type with Text_Type'Class in Create_Text, and your client would get all of them. This would allow a Password_Type as mentioned above. Most of the things in Element.Common are descended from Base_Type, which doesn't have a Create, so you'd have to add others you think useful individually. Many things in Form are descended from Form_Element_Type, so you could provide type Kind_ID is (Button, Checkbox, Color, Date, Datetime, Datetime_Local, Email, File, Hidden, Image, Month, Number, Password, Radio, Range_ID, Reset, Search, Submit, Tel, Text, Time, Url, Week); procedure Create_Form_Item (Item : in out Form_Element_Type'Class; Item_Kind : in Kind_ID; Value : in String := ""; Name : in String := ""; ID : in String := ""); which would call Create_Element with an appropriate String for Input_Type. So you'd have to write a number of these Create operations, but there wouldn't be too many of them. I'd want to limit what kinds of things a client can put in a dialog, and maybe how many of them, too. But probably if you use Gnoga.Gui.Plugin.jQueryUI.Widget.Dialog_Type with Modal => True you can save yourself the effort. -- Jeff Carter "We call your door-opening request a silly thing." Monty Python & the Holy Grail 17 ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-22 21:24 ` Jeffrey R. Carter @ 2017-05-25 3:45 ` Jere 0 siblings, 0 replies; 42+ messages in thread From: Jere @ 2017-05-25 3:45 UTC (permalink / raw) On Monday, May 22, 2017 at 5:24:11 PM UTC-4, Jeffrey R. Carter wrote: > On 05/22/2017 12:46 AM, Jere wrote: > > with Gnoga.Gui.Window; > > with Gnoga.Gui.View; > > package Gnoga.Gui.Modal_Dialog is > > > > type Dialog_Type is tagged limited private; > > type Dialog_Access is access all Dialog_Type; > > type Pointer_To_Dialog_Class is access all Dialog_Type'Class; > > > > procedure Create > > (Dialog : in out Dialog_Type; > > Parent : in out Gnoga.Gui.Window.Window_Type'Class; > > ID : in String := ""); > > > > private > > > > type Dialog_Type is new Gnoga.Gui.View.View_Type with record > > Main_View : Gnoga.Gui.View.View_Access := null; > > end record; > > > > end Gnoga.Gui.Modal_Dialog; > > I don't see any reason for the access type in the full view. It could probably > just be View_Type, but I think it should really be > Gnoga.Gui.Element.Form.Form_Type, as I'll explain later. In my original attempt, I ended up passing back an access value via a function. In order to avoid having to pass an Unchecked_Access from an aliased parameter, I went full access type under the hood. Based on your later example, I scrapped that. > > > The other option I was tossing around was to > > instead add a > > > > procedure Add_Component > > (Dialog : in out Dialog_Type; > > Object : in out Gnoga.Gui.Base.Basetype'Class); > > Mostly what people need in such a dialog are buttons and text widgets > (Text_Type). The former are from Gnoga.Gui.Element.Common and the latter from > Gnoga.Gui.Element.Form, which is why I think your inner view should be a form. > You can create buttons with a form as the parent, but you can't create a text > widget without a form. (This seems wrong to me, but that's how Gnoga is. I've > often used form widgets but never needed the form functionality.) So as a first > cut you could only have > > procedure Create_Button (Button : in out Button_Type; > Content : in String := ""; > ID : in String := ""); > > procedure Create_Text (Text : in out Text_Type; > Size : in Positive := 20; > Value : in String := ""; > Name : in String := ""; > ID : in String := ""; > Label : in String := ""); > > and you'd probably cover 95% of uses. Internally, you'd call Button.Create with > your form as Parent, and Text.Create with your form as Form. Your client can > then attach an on-click handler to the buttons and call Value for the text > widgets. If Label is not null, you would create a label to go with Text with > Label as its Content. > > You might also want to provide a Create_Div that the client could put a bunch of > text into, and a New_Line for putting things on different lines. So you could > have dialogs like > > <snipped> > > So you'd have to write a number of these Create operations, but there wouldn't > be too many of them. I'd want to limit what kinds of things a client can put in > a dialog, and maybe how many of them, too. I get that, and I like the idea. I'm not sure I agree with the limitations suggested. I don't see why doing what you suggest for just a view type wouldn't cover all cases and still limit access to the internals of the tagged type. I ended up thinking on this and playing around with it. I scrapped the internal access type, and I then added a new procedure: procedure Create_Main_View (Dialog : in out Dialog_Type; View : in out Gnoga.Gui.View.View_Type: ID : in String := ""); That way, they can setup whatever view with whatever components they want and my Dialog_Type just supplies the Modal operations to use it. If they have a new custom type already defined, then they can just create an intermediate view to hold it and pass that in. I'm still playing with it as my kids allow me time, but so far it has most of my bases covered. I think my only snag so far has to do with the mechanics of width and height parameters. I'll probably fire up a question in the gnoga list for that since it is more HTML/Gnoga based in nature. I know it isn't exactly what you suggested, but hopefully it is still a good way to go. I don't want to fully limit what types can be used in the dialog because someone might want to do something complex like a portable file chooser dialog (for standalone apps). In cases like those, I would want to be able to at least help support custom widget types. Maybe I am overthinking it though. > > But probably if you use Gnoga.Gui.Plugin.jQueryUI.Widget.Dialog_Type with Modal > => True you can save yourself the effort. I didn't realize they had added that. I'll play with it some to see how it works out. I'm still playing with my handmade one if only to learn more, but this is a good point and I should experiment with the jquery one. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 20:21 ` Jere 2017-05-21 21:09 ` Jeffrey R. Carter @ 2017-05-21 21:20 ` Dmitry A. Kazakov 2017-05-21 21:45 ` Jere 1 sibling, 1 reply; 42+ messages in thread From: Dmitry A. Kazakov @ 2017-05-21 21:20 UTC (permalink / raw) On 2017-05-21 22:21, Jere wrote: > The actual use case has > them in separate packages, but the problem is the same: No. Robert meant that when you declare a subprogram in a separate package [more precisely after the type's freezing point] it will not become a primitive operation. A non-primitive operation is simply not inherited and if not visible, is gone. Being not inherited is a very important point, it will be rejected for derived types even if visible. Consider this: package Base is type Base_Type is tagged null record; end Base; package Base_Something is procedure Something (X : in out Base_Type; Y : Integer) is null; end Base_Something; package Derived is type Derived_Type is new Base_Type with null record; end Derived; package Derived_Something is procedure Something (X : in out Derived_Type; Y : String) is null; end Derived_Something; use Base, Derived, Base_Something, Derived_Something; X : Base_Type; Y : Derived_Type; begin Something (X, 1); -- OK Something (Y, 2); -- Type error! Something (Y, "2"); -- OK -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: Preventing private procedure visibility being made public through extension 2017-05-21 21:20 ` Dmitry A. Kazakov @ 2017-05-21 21:45 ` Jere 0 siblings, 0 replies; 42+ messages in thread From: Jere @ 2017-05-21 21:45 UTC (permalink / raw) On Sunday, May 21, 2017 at 5:20:36 PM UTC-4, Dmitry A. Kazakov wrote: > On 2017-05-21 22:21, Jere wrote: > > > The actual use case has > > them in separate packages, but the problem is the same: > > No. Robert meant that when you declare a subprogram in a separate > package [more precisely after the type's freezing point] it will not > become a primitive operation. A non-primitive operation is simply not > inherited and if not visible, is gone. Being not inherited is a very > important point, it will be rejected for derived types even if visible. > Consider this: > > package Base is > type Base_Type is tagged null record; > end Base; > > package Base_Something is > procedure Something (X : in out Base_Type; Y : Integer) is null; > end Base_Something; > > package Derived is > type Derived_Type is new Base_Type with null record; > end Derived; > > package Derived_Something is > procedure Something (X : in out Derived_Type; Y : String) is null; > end Derived_Something; > > use Base, Derived, Base_Something, Derived_Something; > X : Base_Type; > Y : Derived_Type; > begin > Something (X, 1); -- OK > Something (Y, 2); -- Type error! > Something (Y, "2"); -- OK > > -- > Regards, > Dmitry A. Kazakov > http://www.dmitry-kazakov.de Yes, but in the current case, Base_Type is already defined in a library with the procedures in the same package as the type. However, this is a good point for when I make my own types. ^ permalink raw reply [flat|nested] 42+ messages in thread
end of thread, other threads:[~2018-05-20 12:03 UTC | newest] Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2017-05-20 17:33 Preventing private procedure visibility being made public through extension Jere 2017-05-20 20:13 ` AdaMagica 2017-05-20 21:55 ` Jere 2017-05-20 20:32 ` Dmitry A. Kazakov 2017-05-20 22:51 ` Jere 2017-05-21 0:51 ` Jere 2017-05-21 9:16 ` Chris Moore 2017-05-21 22:55 ` Jere 2017-05-21 8:44 ` Dmitry A. Kazakov 2017-05-21 12:19 ` J-P. Rosen 2017-05-21 12:53 ` Dmitry A. Kazakov 2017-05-21 20:06 ` Jere 2017-05-21 21:07 ` Dmitry A. Kazakov 2017-05-21 22:28 ` Jere 2017-05-22 8:52 ` Dmitry A. Kazakov 2017-05-22 13:33 ` AdaMagica 2017-05-22 13:43 ` AdaMagica 2017-05-22 21:17 ` Randy Brukardt 2017-05-25 4:06 ` Jere 2017-05-25 19:39 ` Randy Brukardt 2017-05-25 22:53 ` Jere 2017-05-25 22:57 ` Jere 2017-05-26 20:46 ` Randy Brukardt 2017-05-26 22:35 ` Simon Wright 2018-05-20 11:22 ` Simon Wright 2018-05-20 12:03 ` Jere 2017-05-26 22:58 ` Jeffrey R. Carter 2017-05-30 21:15 ` Randy Brukardt 2017-06-02 1:07 ` Jere 2017-06-02 7:31 ` Dmitry A. Kazakov 2017-06-02 8:09 ` Mark Lorenzen 2017-06-02 11:31 ` Simon Wright 2017-05-22 21:12 ` Randy Brukardt 2017-05-23 7:38 ` Dmitry A. Kazakov 2017-05-21 18:14 ` Robert Eachus 2017-05-21 20:21 ` Jere 2017-05-21 21:09 ` Jeffrey R. Carter 2017-05-21 22:46 ` Jere 2017-05-22 21:24 ` Jeffrey R. Carter 2017-05-25 3:45 ` Jere 2017-05-21 21:20 ` Dmitry A. Kazakov 2017-05-21 21:45 ` Jere
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox