Proteus Lookup Tables

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.

 

The Base class

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

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.

 

Nested Object Services

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.

 

The proteusServiceFor() Macro

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.