NAME

virtual_method -- make virtual method

PROTOTYPE

unitptr virtual_method( char *pcName, unitptr uOrig, int (*Accept)(unitptr), /* to type check parameter unit */, int (*Notify)(unitptr, /* function to notify of change of used class instance */, char *pcOptions, unitptr uHost)

ARGUMENTS

char *pcName
name of referenced method
unitptr uOrig
unit to provide interface template
int (*Accept)(unitptr)
- not documented in source --
/* to type check parameter unit */
to type check parameter unit
int (*Notify)(unitptr
- not documented in source --
/* function to notify of change of used class instance */
function to notify of change of used class instance
char *pcOptions
options
unitptr uHost
host unit

RETURN VALUE:

A pointer to the created unit or NULL in the case of an error.

INTERFACE OF CREATED UNIT:

According to interface specificaton (see below)

SYNOPSIS:

This unit can only be created as a subunit of a class container. The presence of one or several virtual_method subunits in a class container allow each class container instance to become parametrized with a corresponding number of other named NST units (each virtual_method subunit introduces one such parameter that is selected by specifying pcName). This allows to implement classes that cooperate closely with data in these other NST units (which can be other class units or `simple'' NST units), but in such a way that the cooperating instances of cooperating NST units can be changed dynamically. To restrict the NST units that a virtual_method unit can select as a parameter for its class container, there are two means: (i) the interface can be restricted to match exactly or to be a superset of the interface specified for the virtual_method unit. (ii) the admissible type of selected NST unit can be restricted by specifying an indicator function (each of type (int)(*)(unitptr)). The virtual_method unit will then only be able to select such NST units for which the specified indicator function returns true. To allow a selected unit to become aware of the start or the end of its selection as a parameter, the constructor function allows the specification of two functions attach and release. If not NULL, function attach(u,v) will be called when u becomes selected by the virtual method_unit v. Likewise, release(u,v) will be called, when the selection of u by v ends (e.g., shortly before v is deleted).

REMARKS PERTAINING TO NEO:

Neo displays virtual_method units in class containers in a special way: unlike other class container subunits, they are displayed with a 'm' mark, indicating that they can be `modified''. Selecting the 'm' mark will open a dialog window that allows to remake the virtual_unit with a different name pcName. In this way, it becomes possible to interactively change the parameter units that are associated with a parameterized class.

WHEN TO USE A VIRTUAL METHOD UNIT:

Assume, a class of type T1 needs for its operation access to a second class of type T2. For example, T2 might be a set of nodes (with its own operations to add or delete nodes), and T1 might extend T2 by offering operations to create edges between pairs of nodes; the necessary access of the operations of T1 to the nodes in T2 could then be achieved by making T2 a parameter object for T1 (note that T2 might simultaneously be a parameter object for further classes, such as classes for the creation of edges of a different type) In general, we thus want to offer with T1 not only method calls of the form DoSomethingWithinT1(..), but we also want to use methods requiring an argument parameter for accessing an instance *t2 of a second class T2:

        DoSomethingBetweenT1AndT2(.., T2 *t2)

However, by a suitable design of class T1, all such functions can be implemented in the simpler form

        DoSomethingBetweenT1AndT2(..)

with no explicit T2 argument, and such functions can be wrapped as NST subunits in the usual way. However, we then must provide one additional function that must be called to set or change the t2 instance to which the mixed operations shall refer, say:

        (void*) select_other(T2 *t2); // define t2-instance to be used

If the original class is not designed in this way (i.e., if it offers only mixed argument functions), it is usually very easy to derive from it a class that conforms to the simplified scheme (this can be done, e.g., by modifying the NST wrapper class suitably).

DESCRIPTION:

The virtual_method units take care of the required calls of the select_other - function (which itself is a member of the T1 class). To this end, each virtual_method unit acts as a `parameter placeholder'' in the class container. I.e., for each desired parameter there must be a constructor call for a virtual_method unit (with the class container as host unit) that passes the following information: 1. the name pcName of the NST class container unit that wraps the instance t2 of the other class T2 to be used as parameter 2. a function pointer int(*Notify)(unitptr,unitptr) that wraps the select_other function of class T1 for the virtual_method unit (the wrapper is necessary, since pointers to C++ element functions, such as select_other(), suffer from some restrictions and can -- unlike C function pointers -- not be directly passed as argument parameters to C functions).

A suitable wrapper can be defined as follows:


     void Notify(unitptr u, unitptr v) {
        // will be called with v = NST-class-container-of(t2)
        void *t2 = v ? nst_var_of(v) : NULL; // t2 from class container
        ((T1*) u)->select_other((T2*) t2); // desired C++ function call
     }

   (of course, with the function name .select_other replaced by the
   actual name of the corresponding function of class .T2)
   
3. Optionally parameters to restrict the units that are accepted as wrappers of an admissible instance t2 (cf. below) Then, upon creation of the class container each virtual_method subunit will perform the following steps: 1. search for `its'' associated parameter unit, using pcName as search name 2. if found, test whether it is of suitable type and of suitable interface 3. if ok, it will assume that the found unit wraps a method instance t2 of proper type and size; it will then issue a select_other(t2) call. 4. if the select_other(t2) call returns zero, it is assumed that t2 has been properly set and the task of the virtual_method unit is complete. If steps 1-4 cannot be completed successfully, a corresponding error message is issued.

CHANGES TO THE CLASS PARAMETER UNIT:

What happens when the referenced class parameter unit (the unit that represents the t2 instance of class T2) changes or becomes deleted? NST automatically monitors any such changes (via a global list of units referenced by name) and notifies any affected virtual_method unit (several such units may share a common parameter unit). When the referenced unit becomes inaccessible, the virtual_method u unit will issue the call

      Notify(u,NULL)

and the above wrapper function will call the select_other method which must be built such that class instance t1 does all necessary steps to continue without t2 (e.g., by disallowing further calls to methods requiring the presence of t2). A change to t2 is treated as a removal, followed by a re-apperance of the changed instance, giving rise to two calls

      Notify(u,NULL)
      .Notify(u,vNew)

where vNew is the NST class container of the changed t2 instance.

REQUIREMENTS ON THE SELECT_OTHER FUNCTION:

Since above calls are automatically handled by NST, once the virtual_method unit has been set up, the implementor need not worry about them, except that the design of the select_other() method is such that 1. select_other(NULL) notifies class instance t1 that t2 is absent 2. select_other(t2) notifies class instance t1 that in future calls t2 shall be used. 3. any sequence of such calls must be admissible, with t2 either NULL or a legal pointer to a T2 class instance

REQUIREMENTS ON ADMISSIBLE PARAMETER UNITS:

Before a named unit u is accepted as a candidate to provide a class parameter, it is checked according to one or two criteria. If the exec function of the virtual_method unit has not been changed, the virtual_method will reference unit u (allowing to use u's transformation via invocation of the exec method). It thus will dynamically adjust to the interface of the referenced unit u, which requires to check that the interface is suitable. In this case, therefore the following two criteria must be met: 1. Interface: u must have an interface that `contains'' the interface that was specified for the virtual_method unit. I.e., for each field of the virtual_method unit there must be a corresponding field of u that has the same type and dimension. Additionally, u may (but need not) have further fields of any type. This ensures, that the current interface will alway be equal or larger than the initial interface. 2. Type: u must be such that the function int Accept(unitptr u) that was passed when the virtual_method unit was created returns true with u as its argument. By writing a suitable indicator function, this allows to restrict the admissible units suitably to guarentee that they wrap a class of the correct type. If the indicator function is specified as NULL, no such test is made. If the option string contains the token %f, the unit will keep its original interface. In this case, criterion 1 will not be required (since the unit has always the same interface). When u passes the checked criteria, u is assumed either to (i) wrap a class instance whose address t2 is the desired parameter, or (ii) u is a `normal'' NST unit and it is u itself that is the expected parameter. If nst_wraps_methods(u) is true, case (i) is assumed and the virtual_method unit will assume that the address of the wrapped class instance is then nst_var_of(u). THIS IS A FIXED CONVENTION THAT ANY IMPLEMENTATION MUST CONFORM TO! If nst_wraps_methods(u) is false, case (ii) is assumed and the virtual_method unit will take t2=u.

OPTIONS:

The following options are provided:

%f:
The virtual method is forced to keep its original interface.
%C:
sets transfer policy for input values to ONLY_CONNECTED_PINS. In this case, the use_method unit will transport only from its connected input pins values to the referenced unit. This allows to execute the referenced unit while leaving a subset of the values of its input pins unchanged (note, however, than any changes in the inputs of the referenced unit will only be temporary and are always undone when the use_method unit has finished execution).
%o:
sets transfer policy for output values to BY_REDIRECTION. This means that for the time of its invocation, the referenced method subunit is made to write its output values directly into the output interface of the referencing virtual_method unit This avoids the danger of side effects associated with the faster BY_REFERENCE policy (which is the default). Note that due to the output redirection, the outputs of the referenced method unit will not become changed. This may cause problems with method units that use their output pins as part of their state memory. In this case, one should use the (somewhat slower) policy BY_COPY below.
%O:
sets transfer policy for output values to BY_COPY. This means that the method subunit writes its results into its own output interface and then these values are copied into the output interface of the referencing virtual_method unit This mode is the safest, but also the slowest.

EXAMPLES:

The file nst_llm.C provides an example of the use of the virtual_method unit.

STATUS:

Preliminary.

SEE ALSO:

use_method

FILE

/amnt/loge/users/nistaff02/nistaff/rhaschke/nst7/man/../o.linux//../nstsrc/nst_method.c