Because many of the abstract
datatypes in Proteus make use of lookup tables to store and retrieve object
information ProteusLU provides a static abstract datatype encapsulating a lookup
table and a common API for use by these other modules.
All objects deriving from ProteusLU are initialized using the proteusPreinit preinit object, after the creation of the Symbol
Table has taken place. This means that their encapsulated lookup tables will be
created and assigned to proteusGlobal, Next the tables are loaded during preinitialization
by proteusLUPreinit.
Concrete instances of the ProteusLU class should assign a property pointer to the myValuesProp attribute indicating which property on a given object
contains a list of values that are to be stored in the lookup table.
Additionally, each concrete instance should define a method assigned to &myAddProp that will be used for validating and adding object /
value pairs to the lookup table and assign this method to the lookup table’s myAddProperty attribute.
For example, if we have a
concrete lookup table for interfaces (discussed further on) then we set it up
as follows:
class InterfaceLU: ProteusLU
{
/* lookup
table custom props for Interface */
luCustomProps(Interface)
instantiate()
{
return
new InterfaceLU();
}
construct()
{
inherited();
gInterfaces = self;
}
. . .
/*
* This method should be used if adding an
interface dynamically to
* an object. This method will verify the
interface and object
* compliance first, before adding it to the
lookup table.
*/
addInterface(obj, interface)
{
validateInterfaces(obj,
interface);
validateReqProps(obj, interface);
storeValue(obj, interface);
}
. . .
}
The luCustomProps() macro is defined in proteus.h, and automatically
defines myValuesProp = &myInterfaces,
and defines myAddProp = &addInterface.
Additionally the macro defines myServiceSuffix =
‘InterfaceProteusService’ and myServicesProp = &hasInterfaceProteusSwerviceProp.
The attribute myValuesProp is a pointer to the values list that must be defined
in an object definition in order to add interfaces to the lookup table for the
object. If our object is defined as follows, the values listed in its myInterfaces attribute will be added to the values list stored in
the intefacesLU lookup table for this object.
MyObject:
object
{
myInterfaces = [ MyContainerIF, MySurfaceIF
]
. . .
}
Notice too that interfaceLU defines an addInterfaces()
method and has assigned a pointer to this method to myAddProperty. This is the method that is called when preinit adds
the object / value pair to the lookup table, and is the same method that should
be used when an author’s code adds an object / value pair to the lookup table
during normal processing. This method provides an opportunity to validate the
object / value pair before it is added to the table. In this case an exception
is thrown if the pair fails one of the validation routines.
The luCustomProps() macro does another thing. It defines a myServicesSuffix and myServiceProp attribute for the Lookup Table. In the case of the InterfaceLU, these attributes are set to ‘InterfaceProteusService’ and
&hasInterfaceProteusServiceProp
respectively.
After
any values in the myInterfaces list have been added to the table for a given object, a
message is sent to the object’s myInterfaceServiceProp. If this attribute returns true then each property of the
object is checked by name, if any of them end with ‘InterfaceProteusService’
then they are evaluated. Those attributes that resolve into TypeObject are also added to the Lookup Table.
This
macro, defined in proteus.h, simplifies an author’s task of defining nested
objects to be added to any of the proteus lookup tables. In the case of proteusServiceFor(Interface) this macro first defines a hasInterfaceProteusServiceProp attribute for the object that is set to true. It next
defines a propertyset that appends ‘InterfaceProteusService’ to the name of
each property defined in the propertyset.
As an
example, if we wanted to add an interface to myObject, we could do it one of two ways. Using the list approach:
myObject: object
{
myInterfaces = [MyInterface]
. . .
}
class MyInterface: Interface
{
. . .
}
or
using the nested object approach:
myObject: object
{
proteusServiceFor(Interface)
{
myInterface: MyInterface {}
}
}
In either approach the interface must be defined
elsewhere in the source code. This is only true when defining an interface for
an object. When adding an observer to an observable, or adding a service to a
proxy service the nested object approach can be used to define the observer or
service within the nested object, thus keeping all the code within the parent
object.
This file is part of the TADS 3
Proteus Library Extension
Copyright ©
2001-2004 Kevin Forchione. All rights reserved.