[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

MOP programming - compile vs. load



ACL 4.2beta

Hi all:

I am attempting to implement an extension to CLOS via the MOP and am
having some trouble with compile versus load time execution of class
metaobject methods.

As background, here is a brief explanation of what I am trying to
accomplish.  I have a need to "surround" *every* method of an object
with some body of code.  It is highly desirable that the
implementation mechanism is transparent as possible with respect to
those methods that are surrounded.  I don't believe that defining a
special method metaobject is appropriate for implementing surround
behavior.  Anyway, not all methods of a particular generic function
necessarily have the surround behavior.  I also do not think that
:around methods are the appropriate mechanism for implementing
surround behavior.  The surround behavior is more closely associated
with the behavior of a particular class, not the individual methods.
For class hierarchies, a "mixin" class may be a component of a class
that does not have the surround behavior.  Thus, in this context the
methods associated with the mixin class should not have surround
behavior.  Therefore, I believe that defining a class metaobject is
probably the most appropriate mechanism for implementing surround
behavior.  The class metaobject can then be used to "declare" the
intention that a particular class (and therefore its methods) have
surround behavior.

The ultimate use of this mechanism is to implement what I call "gated"
objects.  Conceptually, gated objects are objects that have a
(Dykstra, Brinch Hansen) "monitor" that surrounds them.  This monitor
moderates concurrent access to a gated object such that one and only
one thread of control can execute a (direct or inherited) method of the
object at any point in time.  Thus, we have an object-oriented
mechanism for controlling concurrent access to shared resources (code
and data).

So, what I have done is to define a metaclass called
encapsulated-class that defines the base behavior upon which gated
objects will be built (so called "surround" or "encapsulated"
methods).  An encapsulated class has a "shadow" class that is its
dual.  For each encapsulated class, there is an encapsulator class.
For each method of the encapsulated class, there is a "trampoline"
method of the encapsulator class that embodies the "surround"
processing.  When a new encapsulated class is defined (via defclass
with a :metaclass of encapsulated-class) a new encapsulator class is
defined behind the scenes (again via defclass).  As methods are
defined for an encapsulated class, "trampoline" methods are defined
for the encapsulator class behind the scenes.

When an instance of an encapsulated class is made, its dual (the
encapsulator) is also made.  What is returned from the make-instance
method for an encapsulated class is actually the encapsulator
instance.  Thus, any methods destined for the encapsulated object are
actually handled by the encapsulator object.  Any "surround"
processing is performed by the encapsulator trampoline method, then
control is passed to the real method in the encapsulated object.  For
gated objects, this involves obtaining a "lock" associated with the
encapsulator object before calling the real method in the encapsulated
object.

OK, all this stuff seems to be working except for one "tiny" problem.
When an encapsulated class is "compiled," it is actually instantiated
(that is the encapsulated-class class metaobject is instantiated).
This causes the initialize-instance method on the encapsulated-class
class metaobject to be called.  This is where I hook in to define the
encapsulated class.  The problem is, at *compile time*, certain data
structures (such as the superclass of an encapsulated class) are ill
defined (the superclass is a forward-referenced-class).  When the
"compiled" code is "loaded", this causes the class metaobject to be
seemingly instantiated again (?) and initialize-instance to be called
again (no, *not* reinitialize-instance).  But my processing (which the
first time thru at compile time did a defclass of the encapsulator
class) now ends up (indirectly) invoking reinitialize-instance on the
encapsulator class (since I do the defclass of the encapsulator class
again as I am unable to distinguish compile time from load time
execution).  What I need to do (I think) is to defer my processing
that is hooked into the initialize-instance method on the
encapsulated-class class metaobject until *not* compile time.  Note
that when I *evaluate* the defclass form of the encapsulated class
(instead of compile it), everything works just fine!  Also, what is
going on at compile and load time such that the initialize-instance
method is called two times - once at compile time then again at load
time?

Whew!  Anybody have any thoughts on this?

Thanks

ba

Brian H. Anderson                     (206) 234-0881
Boeing Commercial Airplane Co.        bha@gumby.boeing.com
Seattle, Wa.