comp.lang.ada
 help / color / mirror / Atom feed
* Limited initialization for non-limited types
@ 2008-03-26 13:26 Eric Hughes
  2008-03-26 14:02 ` Robert A Duff
                   ` (5 more replies)
  0 siblings, 6 replies; 15+ messages in thread
From: Eric Hughes @ 2008-03-26 13:26 UTC (permalink / raw)


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).

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.
Ordinarily, assignment would overwrite the trace object.  Actually, it
still does.  I save and restore the trace object in the Finalize and
Adjust procedures:

    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.  I'll just admit up front that this is a little Evil(TM).  With
this technique, you can get most of the effect of overriding the
assignment operator.  And this implementation doesn't work with
multiple tasks unless you have task-specific storage.  That said, I'm
essentially recreating part of a debugger capability--to attach a
monitor to the value of a variable and observe it.  This sort of white-
box testing has pretty much kept me out of debuggers entirely for a
while now.  And even though it's far from efficient, I'm using it only
in a test environment, not in a production one.  So I won't stop using
this approach.

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 ) ;

The first thing that doesn't work is that A gets the wrong trace
object.  After the call to Adjust, variable A has the same default
trace object it started with.  The temporary had the specified object
T and it's lost.

And it gets worse.  When Initialize is called on A, A has the default
trace object, not T.  The only way to trace the initialization is to
use the default trace object.  That's what I'll be doing (barring
another approach), but I don't like it.  I'm adding a procedure to set
Trash_Stash and will use that during Initialize.

While I have an immediate work-around, I'm writing this in advance of
a next round of language debate.  There are a number of ideas that
come to mind in thinking about alternate ways of dealing with this
problem.

1) Limited-style initialization for non-limited types.  Here's example
syntax:

    A : X := limited Construct_X( T ) ;

The translation would be thus:

    A : X := Construct_X( T ) ; -- as if X were a limited type
    Initialize( A ) ;

It's much more efficient to boot.  Personally, I'd prefer this were
the default, but there are backward-compatibility issues to consider.
I'd also add the following syntax to enforce creation of a temporary:

    A : X := not limited Construct_X( T ) ;


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.

In addition to the kind of testing application I'm doing, I can think
of another important application for this: dynamic binding.  The big
winner in this space has been Java reflection.  I don't like that
interface as written because it exposes everything and is an endless
source of mischief.  But Java reflection has been instrumental in
creating dynamic binding systems that have become so popular in web
applications.  Substitute "N : Name" as a private component in the
example and you have a key technical hook in the language that allows
a dynamic binding system to be known to be safe and correct.


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?


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 ;

Access through type C would be limited.  Access through another
available mechanism might not be limited.


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-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 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 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 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 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-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-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 11:23 ` Martin Krischik
@ 2008-03-28 15:47   ` Eric Hughes
  0 siblings, 0 replies; 15+ messages in thread
From: Eric Hughes @ 2008-03-28 15:47 UTC (permalink / raw)


On Mar 28, 5:23 am, Martin Krischik <krisc...@users.sourceforge.net>
wrote:
> Not that I know of. But have a look here:

Thanks for the references.

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
                   ` (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

end of thread, other threads:[~2008-04-02  4:06 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
2008-03-26 22:13 ` Randy Brukardt
2008-03-27  3:25   ` Eric Hughes
2008-03-28  6:56     ` Randy Brukardt
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
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox