* 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 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: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 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-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 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 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-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 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 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: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 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 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
* 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 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 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-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-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-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-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: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-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-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 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-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
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