* Re: Limited initialization for non-limited types
2008-03-26 13:26 Limited initialization for non-limited types Eric Hughes
@ 2008-03-26 14:02 ` Robert A Duff
2008-03-27 0:07 ` Eric Hughes
2008-03-26 15:08 ` Dmitry A. Kazakov
` (4 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Robert A Duff @ 2008-03-26 14:02 UTC (permalink / raw)
Eric Hughes <eric.eh9@gmail.com> writes:
> Is there any way of getting in-place initialization for non-limited
> objects?
No.
We're thinking about doing that in GNAT as an optimization,
but there's no way to force it. Note that assignment
statements cannot use build-in-place in all cases.
Perhaps you can get what you want by wrapping a nonlimited type in a
limited record?
> So here's the problem. I can't trace the Initialize call
> "correctly". Consider the obvious initialization:
>
> T : Trace ;
> A : X := Construct_X( T ) ;
>
> Because X is not a limited type, this code translates thus (in one
> ordering):
>
> T : Trace ;
> A : X ;
> Initialize( A ) ;
> Temp : X := Construct_X( T ) ; -- as if limited, constructed in
> place
> Initialize( Temp ) ; -- (*)
> Finalize( A ) ;
> A := Temp ;
> Adjust( A ) ;
> Finalize( Temp ) ;
That's not a correct translation. For example, the line marked (*) will
overwrite the result of Construct_X.
To see what GNAT actually generates, use the -gnatD or -gnatG switch.
But there's no guarantee it won't change in future versions!
- Bob
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-26 14:02 ` Robert A Duff
@ 2008-03-27 0:07 ` Eric Hughes
0 siblings, 0 replies; 15+ messages in thread
From: Eric Hughes @ 2008-03-27 0:07 UTC (permalink / raw)
On Mar 26, 8:02 am, Robert A Duff <bobd...@shell01.TheWorld.com>
wrote:
> Perhaps you can get what you want by wrapping a nonlimited type in a
> limited record?
That won't work in this case. The whole point of this test object is
to monitor its internal behavior under assignment. I can't well do
that without assignment.
Eric
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-26 13:26 Limited initialization for non-limited types Eric Hughes
2008-03-26 14:02 ` Robert A Duff
@ 2008-03-26 15:08 ` Dmitry A. Kazakov
2008-03-26 22:13 ` Randy Brukardt
` (3 subsequent siblings)
5 siblings, 0 replies; 15+ messages in thread
From: Dmitry A. Kazakov @ 2008-03-26 15:08 UTC (permalink / raw)
On Wed, 26 Mar 2008 06:26:42 -0700 (PDT), Eric Hughes wrote:
> Is there any way of getting in-place initialization for non-limited
> objects?
[...]
It is both simpler and more complex. What is IMO needed:
1. Extensible operations. (That is when a primitive operation has
non-overridable parts, like constructors, destructors, assignments,
aggregates usually do.)
2. Assignment (a part of) as a doubly dispatching primitive operation. (In
order to have access to LHS and RHS, which is one problem in your example)
3. Constructor (a part of) as a primitive operation. (Again, to access LHS,
which is another problem you have)
4. Multi-stage construction/assignment model. (In order to handle
discriminants before allocation, allocation before components
initialization and components before assignment completion)
----------
P.S. Supertypes, classes for all types, multiple inheritance. These will
give you a way to hang your tracing stuff on any existing type rather than
deriving them from a common base.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-26 13:26 Limited initialization for non-limited types Eric Hughes
2008-03-26 14:02 ` Robert A Duff
2008-03-26 15:08 ` Dmitry A. Kazakov
@ 2008-03-26 22:13 ` Randy Brukardt
2008-03-27 3:25 ` Eric Hughes
2008-03-26 23:00 ` Lucretia
` (2 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Randy Brukardt @ 2008-03-26 22:13 UTC (permalink / raw)
"Eric Hughes" <eric.eh9@gmail.com> wrote in message
news:b481036a-41ee-4fc5-a0d0-c73cfef60234@s19g2000prg.googlegroups.com...
> Is there any way of getting in-place initialization for non-limited
> objects? This would be similar to that for limited types as specified
> in ARM05/7.5(8.1/2).
There's no way to require it. And I wouldn't expect that to change -
*requiring* implementations to be able to call a single function with
different return mechanisms seems way over the top. (Of course,
implementations are *allowed* to do that if they like.)
There is a second reason: implementations are allowed to optimize
non-limited controlled types in almost any way that is "correct" (that is,
an initialized object gets finalized). OTOH, limited controlled types are
not allowed to be optimized at all. Even if if the object is never directly
used.
The net effect is that the operations of non-limited controlled types cannot
depend on the order that the operations will be called (anywhere), while
limited controlled can always be depended on. Such dependence in non-limited
controlled is just wrong; it's likely to break if the code is moved to a
different compiler (or even a newer version of the same compiler, as Bob
points out).
> Unfortunately, this is not a hypothetical question. I'm writing a
> test object used within the unit tests a certain package. The test
> object is instrumented for white-box testing; it contains a trace
> object as a component. I want to trace the three events of
> Ada.Finalization.Controlled. The trace object is associated with the
> variable itself, not with any value that the variable might contain.
I'd probably suggest finding a different approach. (Not that I can think of
one off-hand.)
...
> 2) Variable-specific record components. Consider this:
>
> type X is new Ada.Finalization.Controlled with
> record
> -- value components (ordinary)
> private
> -- variable components (persistent)
> T : Trace ;
> end record ;
>
> The idea is that components in the private section are simply not
> copied under assignment.
Interesting, but sounds messy to implement, because it would complicate the
"bit-copy" part of the assignment. That's not usually done
component-by-component!
> 3) Anonymous limited types. I don't see any particular reason why the
> semantics couldn't be worked out for the following declaration:
>
> B : limited Any_Type := Initial_Value_Function ;
>
> The ARM is littered with references to phrases such as "the limited
> view of a type". What are the semantic difficulties with this? For
> single declarations they seem to be modest. Isn't this almost the
> same as an in-but-not-out parameter to a procedure?
Anonymous types are a cancer as it is, we surely don't need any more of
them. (Besides, they rarely actually work, since it's usually the case that
you need to write a type conversion or membership somewhere, and you can't
do that without a name.)
> 4) Derived limited types. The ARM states (roughly) that a limited
> type must derive from limited types. But what about a type that is
> explicitly a limited view of a possibly not-limited type?
>
> type C is overriding limited new Any_Type ;
That's already possible for untagged types - any limited private type can be
completed by a non-limited type (including a derivation of some other type).
For tagged types, it would lead to a mess because you could use the
class-wide root type to copy limited extension components. (And if you try
to ban such components, you're going to have to break privacy.)
So, just write:
package P is
type C is limited private;
-- Operations on C.
private
type C is new Any_Type;
end P;
Randy.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-26 22:13 ` Randy Brukardt
@ 2008-03-27 3:25 ` Eric Hughes
2008-03-28 6:56 ` Randy Brukardt
0 siblings, 1 reply; 15+ messages in thread
From: Eric Hughes @ 2008-03-27 3:25 UTC (permalink / raw)
On Mar 26, 4:13 pm, "Randy Brukardt" <ra...@rrsoftware.com> wrote:
> There's no way to require it. And I wouldn't expect that to change -
> *requiring* implementations to be able to call a single function with
> different return mechanisms seems way over the top.
The real goal I'm looking for is to suppress the extra calls to
Initialize/Adjust/Finalize. The main obstacle is that the call to
Initialize which has the "right" initial values is in the function
body and the call to Initialize which has the ultimate 'Access value
is in the declaration. For limited types, I presume this implies that
the 'Access value is passed implicitly to the function. Hence for
this to work at all there would have to be restrictions on the
function used for initialization. The exact restrictions are beside
the main point here, only that there is some restriction, which, then,
would require some kind of declaration on an acceptable function. (I
didn't realize this when I first posted.)
So this critique is well taken. It seems that a necessary component
of a solution is to require a certain kind of return mechanism.
> There is a second reason: implementations are allowed to optimize
> non-limited controlled types in almost any way that is "correct" (that is,
> an initialized object gets finalized).
OK. So there's a specification about how initialization works
correctly. I had assumed (silently) that this specification would be
expanded to have correct results both for assignment (including
initial assignment) and for initialization, and that they would not be
identical. So this implies an enlarged specification. I would assume
that the same optimization principle would apply to this new piece of
specification.
There's a syntactic ambiguity between ":=" for initial assignment and
":=" for initialization. In retrospect, I find it unfortunate the
same symbol was used. New syntax would be required to distinguish
these for non-limited types.
> I'd probably suggest finding a different approach. (Not that I can think of
> one off-hand.)
Well, I have an approach that will work, but it's pretty ugly. I
posted in the hope that something would eventually be cleaner.
Reviewing:
1) Declaration of a function suitable for non-assignment
initialization.
2) New specification of controlled behavior with such initialization.
3) Syntactic distinction between assignment and initialization.
I wasn't looking for this when I started this thread, but isn't this
exactly what's needed to define a proper constructor function?
> > 2) Variable-specific record components. [...]
>
> Interesting, but sounds messy to implement, because it would complicate the
> "bit-copy" part of the assignment. That's not usually done
> component-by-component!
I see three consequence with this idea. First, there's an existing
assumption that the copy-length is the same as the allocation-length.
I presume that all existing compilers conflate these (since there's no
existing reason not to). The other assumption in this area is that
the offset of the modifiable portion is zero. I see no reason to keep
this as a default, but it's not necessarily so in general. My first
pass recommendation is that the default layout in memory be two
records, one for each half, allocated contiguously, with each part
aligned on whatever natural word boundary the compiler already uses.
With this layout, record copy is equally efficient as the existing
case (except, perhaps, where both lengths must be passed implicitly
for some reason).
The second consequence is its "interference" with representation
clauses. With an arbitrary record layout, let's just assume which
includes bit fields, you'd need to generate a read-lvalue/mask-with-
rvalue/write sequence to substitute for a straight copy. It's more
code, and it might be tricky to get working perfectly, but there's
nothing particularly mysterious about it. I'd guess this is where the
heavy lifting is.
The third consequence is that record extensions would require multiple
block memory copies. That means more internal accounting and some
extra code size.
> > 4) Derived limited types. [...]
> For tagged types, it would lead to a mess because you could use the
> class-wide root type to copy limited extension components. (And if you try
> to ban such components, you're going to have to break privacy.)
I'll save this discussion for another day. I don't consider my
thoughts about it sufficiently well-formed yet for a proper
discussion.
Eric
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-27 3:25 ` Eric Hughes
@ 2008-03-28 6:56 ` Randy Brukardt
2008-03-28 15:25 ` Eric Hughes
0 siblings, 1 reply; 15+ messages in thread
From: Randy Brukardt @ 2008-03-28 6:56 UTC (permalink / raw)
"Eric Hughes" <eric.eh9@gmail.com> wrote in message
news:05336885-5b45-4b2d-b27c-733c2002ca58@s13g2000prd.googlegroups.com...
...
> Reviewing:
> 1) Declaration of a function suitable for non-assignment
> initialization.
> 2) New specification of controlled behavior with such initialization.
> 3) Syntactic distinction between assignment and initialization.
>
> I wasn't looking for this when I started this thread, but isn't this
> exactly what's needed to define a proper constructor function?
Exactly, but I think the chances of that happening in Ada are pretty close
to zero. We tried a number of such approaches during the development of the
Amendment, and we were unable to come up with compelling advantages to a
large pile of new syntax and semantics (other than avoiding a nasty
incompatibility). We eventually decided to go with the incompatibility
instead.
Moreover, I was one of those that was not originally convinced that a
function makes an adequate constructor. But I wasn't able to convince
others, and eventually you have to compromise and move on.
Anyway, the Ada model for non-limited types is to allow compilers to do
extensive optimization of assignment (or not). Code that requires a
particular sequence of calls is wrong (even though it might work).
I still think that your entire approach is wrong. I'd probably either try
something using a mixin generic, or more likely simply with a bit debugging
code built into the base class. (An extension would also work in some
cases.)
...
> The third consequence is that record extensions would require multiple
> block memory copies. That means more internal accounting and some
> extra code size.
It would be quite a bit worse than that on some compilers. Combined with
build-in-place and extension aggregates, you probably wouldn't even be able
to figure out where the components are. (One implementation already has a
problem with this.)
I also wonder what dragons luck in this idea. A lot of good ideas simply
don't work in Ada, because they run afoul of problems with discriminant
dependent components or privacy or the generic contract model or some
combination. It would take quite a bit of thought to ensure that such
problems don't lurk here.
Randy.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-28 6:56 ` Randy Brukardt
@ 2008-03-28 15:25 ` Eric Hughes
2008-03-28 21:53 ` Randy Brukardt
2008-04-02 3:00 ` Eric Hughes
0 siblings, 2 replies; 15+ messages in thread
From: Eric Hughes @ 2008-03-28 15:25 UTC (permalink / raw)
On Mar 28, 12:56 am, "Randy Brukardt" <ra...@rrsoftware.com> wrote:
> Moreover, I was one of those that was not originally convinced that a
> function makes an adequate constructor. But I wasn't able to convince
> others, and eventually you have to compromise and move on.
I haven't been able to find a smoking-gun example that you can't work
around with existing syntax. I'm keeping my eye out for one, though.
The best I know of now is that it's problematic to eliminate default-
value initialization at compile-time (easy to do at run-time). This
is useful only for types where no default value is sensible (such as
not-null-access types) and it's only relevant within a private
implementation (by eliminating public visibility of components). On
the other hand, it's a missing aid to an implementer--in the same
class of aid as "type Index is new Integer"--not strictly necessary,
but a felicity of the language.
As to the technical part of the idea, I'll propose "::=" as token for
the initialization operator.
As to the build-in-place requirement, it seems part of a larger idea
I've been mulling over, which is a more general representation
facility. As an example, in an ideal world, I'd like to have the
Convention, Import, and Export pragmas become ordinary code. The core
theory is in Tony Hoare's paper "Proof of Correctness of Data
Representations" (1972). Its essence is a formal way of mapping
between two different representations with equivalent ultimate
effect. The largest visible change to Ada (or to any other language)
would be to make identifiers both for representation languages (name
spaces) and for representation conventions (maps between them). I
doubt this will happen any time soon, though, since it seems a solid
research project to develop the necessary pragmatic theory to apply
this to a real language.
> Code that requires a
> particular sequence of calls is wrong (even though it might work).
Well, I'm not looking for any particular sequence of calls, really.
My immediate application is to verify invariants in run-time by
trapping on particular events (right now it's those of Controlled).
> I still think that your entire approach is wrong. I'd probably either try
> something using a mixin generic, or more likely simply with a bit debugging
> code built into the base class.
I looked at initialization first because it's more widely applicable
outside of my immediate need, for which I think type discriminants may
work. A discriminant whose access_definition is a handle to ordinary
record seems to allow most everything I can think I need soon.
[re: private components]
> It would take quite a bit of thought to ensure that such
> problems don't lurk here.
Granted.
Eric
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-28 15:25 ` Eric Hughes
@ 2008-03-28 21:53 ` Randy Brukardt
2008-03-28 23:37 ` Eric Hughes
2008-04-02 3:00 ` Eric Hughes
1 sibling, 1 reply; 15+ messages in thread
From: Randy Brukardt @ 2008-03-28 21:53 UTC (permalink / raw)
"Eric Hughes" <eric.eh9@gmail.com> wrote in message
news:8ce4085b-08c9-4f3b-9909-3a68b8832194@s8g2000prg.googlegroups.com...
> On Mar 28, 12:56 am, "Randy Brukardt" <ra...@rrsoftware.com> wrote:
...
> > I still think that your entire approach is wrong. I'd probably either
try
> > something using a mixin generic, or more likely simply with a bit
debugging
> > code built into the base class.
>
> I looked at initialization first because it's more widely applicable
> outside of my immediate need, for which I think type discriminants may
> work. A discriminant whose access_definition is a handle to ordinary
> record seems to allow most everything I can think I need soon.
Yes. The other thing I was thinking was that you might be better served by
wrapping your trace object access in a (second) controlled object. That way,
you can use the trace object's Initialize to do your needed setup before the
call of the outer object's Initialize. You'd probably need an access
discriminant somewhere to provide access to the parent object.
Randy.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-28 21:53 ` Randy Brukardt
@ 2008-03-28 23:37 ` Eric Hughes
0 siblings, 0 replies; 15+ messages in thread
From: Eric Hughes @ 2008-03-28 23:37 UTC (permalink / raw)
On Mar 28, 3:53 pm, "Randy Brukardt" <ra...@rrsoftware.com> wrote:
> The other thing I was thinking was that you might be better served by
> wrapping your trace object access in a (second) controlled object.
I had already done that. The testing objects use both a trace and a
variable name: a trace to record (interleaved) events and a name to
distinguish between their disparate origins. Since the trace is
shared, it needed a handle/body implementation.
> You'd probably need an access
> discriminant somewhere to provide access to the parent object.
As of yet, I haven't needed that, since the trace is only a receiver
of event entries.
Eric
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-28 15:25 ` Eric Hughes
2008-03-28 21:53 ` Randy Brukardt
@ 2008-04-02 3:00 ` Eric Hughes
1 sibling, 0 replies; 15+ messages in thread
From: Eric Hughes @ 2008-04-02 3:00 UTC (permalink / raw)
On Mar 28, 9:25 am, I wrote:
> A discriminant whose access_definition is a handle to ordinary
> record seems to allow most everything I can think I need soon.
Except this doesn't work at all. The problem is that it completely
breaks assignment (duh), giving a constraint error at run-time. I'm
back to working with hacking up the equivalent of private record
components. Proper initialization is still the problem, particular
initializing exactly one variable with its unique name. (I finally
figured out I was seeing extra trace entries for the temporaries
mimicking as the variable-of-interest during the initialization-with-
an-ordinary-function assignment.)
Eric
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-26 13:26 Limited initialization for non-limited types Eric Hughes
` (2 preceding siblings ...)
2008-03-26 22:13 ` Randy Brukardt
@ 2008-03-26 23:00 ` Lucretia
2008-03-28 11:23 ` Martin Krischik
2008-04-02 4:06 ` Eric Hughes
5 siblings, 0 replies; 15+ messages in thread
From: Lucretia @ 2008-03-26 23:00 UTC (permalink / raw)
On Mar 26, 1:26 pm, Eric Hughes <eric....@gmail.com> wrote:
> Is there any way of getting in-place initialization for non-limited
> objects? This would be similar to that for limited types as specified
> in ARM05/7.5(8.1/2).
I didn't read your entire post as it's not necessary.
Anyway, it makes sense to do in place initialisation for limited types
as they cannot be copied, therefore a return statement that copies the
object just doesn't make sense whereas one which copies a non-limited
type does.
Obviously there is overhead in the non-limited case but what you have
to understand is that in the limited case it's more semantics and
making sure that the language doesn't create copies for them.
Luke.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-26 13:26 Limited initialization for non-limited types Eric Hughes
` (3 preceding siblings ...)
2008-03-26 23:00 ` Lucretia
@ 2008-03-28 11:23 ` Martin Krischik
2008-03-28 15:47 ` Eric Hughes
2008-04-02 4:06 ` Eric Hughes
5 siblings, 1 reply; 15+ messages in thread
From: Martin Krischik @ 2008-03-28 11:23 UTC (permalink / raw)
Eric Hughes schrieb:
> Is there any way of getting in-place initialization for non-limited
> objects? This would be similar to that for limited types as specified
> in ARM05/7.5(8.1/2).
Not that I know of. But have a look here:
http://mkutils.googlecode.com/svn/trunk/Source/takecmd/takecmd-trace.adb
http://mkutils.googlecode.com/svn/trunk/Source/takecmd/takecmd-trace.ads
http://adacl.svn.sourceforge.net/viewvc/adacl/trunk/adacl/Include/adacl-trace.adb?view=markup
http://adacl.svn.sourceforge.net/viewvc/adacl/trunk/adacl/Include/adacl-trace.ads?view=markup
Regards
Martin
--
mailto://krischik@users.sourceforge.net
Ada programming at: http://ada.krischik.com
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Limited initialization for non-limited types
2008-03-26 13:26 Limited initialization for non-limited types Eric Hughes
` (4 preceding siblings ...)
2008-03-28 11:23 ` Martin Krischik
@ 2008-04-02 4:06 ` Eric Hughes
5 siblings, 0 replies; 15+ messages in thread
From: Eric Hughes @ 2008-04-02 4:06 UTC (permalink / raw)
On Mar 26, 7:26 am, I wrote:
> package Foo is
> type X is new Ada.Finalization.Controlled with
> record T : Trace ; end record ;
> Trace_Stash : Trace ;
> end Foo ;
> ...
> package body Foo is
> procedure Finalize( Object : in out X ) is begin
> Trace_Stash = Object.T ;
> Object.T.Trace_Finalize ;
> end ;
> procedure Adjust( Object : in out X ) is begin
> Object.T = Trace_Stash ;
> Object.T.Trace_Adjust ;
> end ;
> end Foo ;
OK, figuring out initialization was blindingly easy once I got the
right trick. Consider the following additional code in package Foo:
package Foo
[...]
type Nil is null record ;
function Construct_X( T : Trace ) return Nil ;
end Foo ;
...
package body Foo is
function Construct_X( T : Trace ) return Nil is begin
Trash_Stash = T ;
return Nonce : Nil ;
end ;
function Initialize( Object : in out X ) is begin
Object.T = Trace_Stash ;
Object.T.Trace_Initialize ;
end ;
end Foo ;
Why do I have a constructor function for X that returns Nil? Here's
the sample code:
declare
T : Trace ;
NA : Nil := Construct_X( T ) ;
A : X ;
begin
...
What happens is that the Nil constructor has a side effect upon a
package variable, which is then used in the default initialization of
variable A. The realization I needed was that I could use a trivial
constructor function within a declarative_part--where I would have
otherwise used a procedure--to get side effects by which default
initialization would work. That part of this trick is applicable in
other code.
My actual code is more involved. Here's an actual snippet, lifted
from a unit test:
[declarative_part]
NA : Nil := Construct_Traced_Existence( Name => "A", T => T ) ;
A : Traced_Existence ;
NB : Nil := Construct_Traced_Existence( Name => "B", T => T ) ;
B : Traced_Existence ;
[handled_sequence_of_statements]
A := B ;
The trace that comes back (it's generated at run-time) is "(A.I)(B.I)
(A.F)(A.A)", just like you'd expect. The previous method I had tried,
which used ordinary constructor functions, that one didn't work well
at all.
Eric
^ permalink raw reply [flat|nested] 15+ messages in thread