comp.lang.ada
 help / color / mirror / Atom feed
From: Tucker Taft <stt@averstar.com>
Subject: Re: Constructors/Destructors in Ada95
Date: Thu, 26 Oct 2000 17:31:55 -0400
Date: 2000-10-26T21:31:55+00:00	[thread overview]
Message-ID: <39F8A2CB.CA319F5@averstar.com> (raw)
In-Reply-To: 8t6vhc$fmb$1@nnrp1.deja.com

mark.biggar@trustedsyslabs.com wrote:
> ...
> 
> One of the problems with this proposal (redefineing ":=") is that
> you would have to define Initialize, Adjust and Finialize anyway
> as you need them to implement value parameter passing, temporary
> creation and function return values correctly.  Each of those is
> like assingment, but not exactly the same being, built out of the
> three primitives in different ways.  So it makes more sense to just
> define the three primitives and have the compiler generate
> standard usage sequences then to redefine ":=" and have strange
> and hard to understand things happen.

This is part of the reason we did not allow the user
to directly define ":=".  Another important reason has to
do with assignment of composite objects (e.g. records
and arrays) containing objects with user-defined
assignment.  One desirable feature is that the assignment
for a record, for example, is defined in terms of the
assignment of its individual components.  Unfortunately,
there are cases of record assignment in Ada where
the left-hand-side doesn't contain a component that
the right-hand-side does.  This happens if the left-hand-side
is an unconstrained record with discriminants, and the
right-hand-side happens to have different values for the
discriminants than the values of the left-hand-side.
As a result of the assignment, some components of the
left-hand-side may disappear, and some may come into existence.

For example:

    type Rec(B : Boolean := False) is record
        X : Cool_Controlled_Type;
        case B is
            when True =>
               Y : Fancy_Controlled_Type;
            when False =>
               Z : Another_Controlled_Type;
        end case;
    end record;

    R1 : Rec;  -- Defaults to B => False
    R2 : Rec(B => True);

     ...

    R1 := R2;  -- R1.Z disappears, R1.Y is created,
               --  R1.X overwritten

It would be tricky at best to define how or whether
user-defined ":=" for Cool_, Fancy_, and Another_Controlled_Type
should be used in the above case.  However, with the
user-defined operations broken down into Finalize and Adjust,
it is relatively straightforward to specify what happens:
R1.Z is finalized, R1.Y is adjusted after copying from R2.Y,
and R1.X is first finalized, and then adjusted after copying
from R2.X.

One viable semantics would be to specify that user-defined
assignment is used only for those cases where the component
already exists, and is being overwritten, and finalize
or adjust is used for the other cases.  That could work,
though it would prevent using block copy.  With the current
approach, block copy can be used to actually copy the "bits"
from R2 to R1, so long as any appropriate finalizations
are done before the copy, and any appropriate adjusts are
done afterward.  If the record has a bunch of non-controlled
components, then being able to use block copy might be seen
as an important advantage of the Ada 95 approach. 

Another nice feature of the current mechanism is that the 
compiler can create a single "whole record finalize" and another
"whole record adjust" procedure, which are simply "roll ups"
of the per-component finalize/adjust operations, possibly with
an implicit "case" statement based on the current value of
the discriminants.  An assignment statement would then
involve a call on the whole-record finalize, followed
by a block copy, followed by a call on the whole-record adjust.
(Also note that an explicit initialization in a declaration
"looks like" an assignment statement, but it couldn't use
the user-defined assignment, because the left-hand side isn't
yet initialized.  It really just needs a copy and adjust.
No finalizes should be performed.)

Creating an equivalent "whole-record-assign" rollup would
certainly be possible, though the details of such a routine
for a record type like "Rec" above would be pretty painful.
You would compare the values of the discriminants of the
left and right sides, and then depending on the various
possibilities, one would do one of three things for each
component: finalize LHS component, copy RHS into LHS and adjust,
or user-defined-assign RHS into LHS.  The order of doing 
this would be somewhat tricky, and there are sometimes 
"implicit" components that need copying as well.  Finally,
somebody has to do the constraint check when the left-hand
side is *not* unconstrained, handle the case of
self-assignment, handle the case of slice assignments
(deciding between left-to-right or right-to-left based
on relative start addresses) and there are issues of
abort deferral, etc.

Despite all the above "moaning and groaning," it still
might be useful to allow user-defined assignment, even if
it is only used in certain cases (e.g. LHS exists, and
is initialized, and will still exist after the assignment).
Presumably the user-defined assignment would take over
the constraint check, and perhaps the check for self-assignment.
I suppose it might be nice for the compiler to generate the
check for self-assignment, since it is needed at most
once for an assignment statement (it is not needed on
each component), and it will often be possible to optimize
it away.  In most cases, the user-defined assignment would
just be an optimization.  However, because it would take
over the constraint check, the user-defined assignment
could also take on the job of implicit subtype conversion
if desired.  E.g., if you have a discriminated type like:

   type Text(Max : Natural) is record
       Data : String(1..Max);
   end record;

a user-defined assignment routine could automatically
pad or chop on assignment, rather than just complaining
about mismatched discriminants.

One final advantage of the Ada 95 approach has to do
with maintenance.  If the user provides the assignment
operation, then presumably they have to individually
assign each of the components.  If a new component is
added, there is no automatic handling of the new component,
and it might be forgotten in the user-defined assignment
operation.  By contrast, with the finalize/adjust approach,
the finalize for a record type doesn't need to call the
finalize operation for each component -- that happens
automatically.  The user-defined finalize for a record
type only needs to worry about finalizations over and
above those required for the individual components.
Ditto for adjust.  

I suppose for user-defined assignment, one could provide 
some way to get access to the underlying predefined
assignment operation (which omits calls on finalize/adjust 
for the record type), so that one could use that to handle
copying all the components, before or after some special
work is done at the record level.  E.g., one might save
the value of some component of the LHS, then do the
predefined assignment of the RHS into the LHS, and then
do a deep copy of the new value of the component potentially
reusing space allocated for the saved component value.
This would presumably be more efficient than doing the
finalize and adjust, which by necessity need to operate
independently of one another.


> 
> There's a reason that C++ has several types of constructors and in
> many ways the Ada mechanism is simpler.

Also, C++ doesn't have discriminants, at least not
explicitly (users sometimes define structs with
an array at the end that can be extended), so the
problem of "composing" a struct assignment out of the
assignment for the individual components is not
as difficult for the compiler.

> 
> --
> Mark Biggar
> mark.biggar@trustedsyslabs.com

-Tucker Taft
-- 
-Tucker Taft   stt@averstar.com   http://www.averstar.com/~stt/
Technical Director, Commercial Division, AverStar (formerly Intermetrics)
(http://www.averstar.com/services/IT_consulting.html)  Burlington, MA  USA



  parent reply	other threads:[~2000-10-26 21:31 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2000-10-18  0:00 Constructors/Destructors in Ada95 Francois Godme
2000-10-19  0:00 ` Ted Dennison
2000-10-19  0:00 ` Marin David Condic
2000-10-19  0:00 ` tmoran
2000-10-19  0:00   ` Francois Godme
2000-10-19  0:00     ` Ted Dennison
2000-10-20  0:00     ` Tucker Taft
2000-10-20  0:00       ` Francois Godme
2000-10-21  0:00         ` Marin David Condic
2000-10-23  0:00       ` Francois Godme
2000-10-24  0:00         ` Ray Blaak
2000-10-25  0:00           ` Francois Godme
2000-10-25  0:00           ` Marin David Condic
2000-10-25  0:00             ` dmitry6243
2000-10-25  0:00               ` mark.biggar
2000-10-26 11:44                 ` dmitry6243
2000-10-26 13:25                   ` Robert A Duff
2000-10-27  8:10                     ` dmitry6243
2000-10-26 17:55                   ` tmoran
2000-10-27  8:10                     ` dmitry6243
2000-10-26 21:31                 ` Tucker Taft [this message]
2000-10-27  8:46                   ` dmitry6243
2000-10-25  0:00               ` Pascal Obry
2000-10-26  0:00                 ` dmitry6243
2000-10-27  7:12             ` Ray Blaak
2000-10-27 18:11           ` Francois Godme
2000-10-30 11:36             ` Robert A Duff
2000-10-30 22:03               ` dale
2000-10-22  0:00     ` rwilson007007
2000-10-22  0:00       ` Francois Godme
2000-10-24  0:00         ` rwilson007007
  -- strict thread matches above, loose matches on Subject: below --
2000-10-29 22:51 rwilson007007
2000-10-30  4:03 ` Ray Blaak
2000-10-30 12:13 ` Marin David Condic
2000-10-30 16:39   ` Randy Brukardt
replies disabled

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