From: Kaz Kylheku
Subject: Re: CLOS object clean-up
Date: 
Message-ID: <1109955217.165963.302430@f14g2000cwb.googlegroups.com>
Jeff wrote:
> I'm not necessarily referring to garbage collection in the subject.
I'm
> not quite the CLOS guru that I'd like to be (yet), but was showing a
> co-worker some of the many benefits of using Lisp earlier this
week...
>
> As the demonstration went on (with many oo's and ah's, minus the
grunts
> about parens, of course) the conversation turned to OOP. He asked
about
> object cleanup (destructors) and I let him know that it wasn't an
issue
> because of the garbage collector.
>
> This was fine until later he asked how a certain situation would be
> handled in a game we're working on. To simplify the problem, imagine
> the following snippet of C++ code that exists in an engine we have:
>
> ai_controller::~ai_controller() {
>   mesh->ragdoll();
> }

This is a potentially bad design. The mesh object's ragdoll() method
has to be careful not to use any back-reference to the AI controller,
which is undergoing destruction. Ah, C++, what a @#$% mess.

While a C++ object is being destroyed, it changes type! If you call
virtual functions on one of its bases that are still live, they do
wacky things, like not go to the most derived class, but to the most
derived non-destroyed class.

> 'mesh' is a member of ai_controller that isn't destroyed when the
> ai_controller is. The destructor is called when the AI controlled NPC
> dies, and therefore causes the mesh (elsewhere in memory) to ragdoll.

Ragdoll means what, that the visual representation of the mesh will
collapse in the face of virtual gravity?

> He asked how this could be done in CLOS since there is no knowledge
of
> when the ai_controller object is actually released from memory. I
> didn't know the answer, and it peaked my curiosity.

My answer would be that since memory is managed for you in Lisp, you do
not tangle your program semantics with memory management semantics.
That is to say, you don't depend on some important semantic action to
be taken in your software in chronological connection with some memory
management event. Even in the C++ case, you admit that you don't know
when that event will happen.

Your best bet is to actually model the ``death'' of the AI controller
in some way that is not connected to memory management. That is to say,
you have to be able to change the AI controller to a dead state without
obliterating it as an object. For a career C++ programmer, it may be a
huge leap in imagination to separate the metaphor of ``AI controller
death'' from ``block of memory death'', but really, they are two
different things. :)

The fact is, however, that the destructor in C++ is called in response
to an explicit operator call in the program! Something had to invoke
the delete operator on a pointer that object. Or perhaps the object was
implicitly deleted with the  termination of a block scope. So in fact
you do know when the AI controller will die: you can put your finger on
the spot in the program where this is invoked.  And in that exact spot
in the code, you can add the code ``ai_manager->die()'' instead of, or
in addition to, calling the destructor. Your die() function can invoke
the ragdoll() thing, etc. And then you add a garbage collector to C++
to deal with the actual memory releasing. So that gives you a possible
pattern for translating the logic directly to Lisp.

When C++ programmers are /really/ not sure when something will be
released, one tool they reach for is reference counting. Calls to
operator delete are replaced by some release() method, which bumps down
the count, and (guess what) calls the delete operator when it hits
zero. Again, you can put your finger on the spot in the code where the
deletion happens, and you can replace it by any code you want, and
leave the actual memory cleanup to the garbage collector. Anything that
is to be done by the destructor can be done directly in release() just
before calling the destructor. And the advantage of doing that even in
C++ is that you haven't begun destroying the object yet, so any
references to it still behave sanely while the cleanup is happening.
Virtual functions to go the right places, etc.

So basically it boils down to having some representation of AI
controller death, and knowing where and when to call it, which is
probably obvious in the C++.

If you want block scope cleanup actions, use UNWIND-PROTECT, or macros
wrapped around it, patterned after WITH-OPEN-FILE.

Lastly, if you want finalization actions carried out when an object is
garbage collected, investigate your Lisp implementation's finalization
mechanisms. These aren't part of the language, but an extension. You
still don't know exactly when a finalization action will be performed,
so this isn't appropriate when you need events to happen on time, like
a mesh ``ragdolling''  immediately when a controller goes into a death
state.

> While the above is a simple problem, and could be solved by numerous
> other means (read: don't get fixated on this specific problem), it
> really got me thinking as to whether or not there is a "destructor"
> method in CLOS that can be used to do the equivelant of above.

So the answer is, if there was such a method, who would call it? Just
like the delete operator in C++, it would be called explicitly. You
could always locate all the places in the program where it is called.

In the case of objects that you'd want associated with block scopes,
you'd probably cob together some macro called WITH-BLOCK-SCOPE-OBJECT
that would handle the task of calling that function automatically for
the specified objects, regardless of how that block terminates.
From: Joe Marshall
Subject: Re: CLOS object clean-up
Date: 
Message-ID: <4qfr4a6f.fsf@ccs.neu.edu>
"Kaz Kylheku" <ยทยทยท@ashi.footprints.net> writes:

> While a C++ object is being destroyed, it changes type! 

If only C++ supported `UPDATE-INSTANCE-FOR-DELETION'...

> When C++ programmers are /really/ not sure when something will be
> released, one tool they reach for is reference counting. 

That way they can be sure that *no one else* knows, either.