NAME
virtual_method -- make virtual method
PROTOTYPE
unitptr virtual_method( const char *pcName, unitptr uOrig, int (*Accept)(unitptr), (*Notify)(unitptr, /* function to notify of change of used class instance */, const char *pcOptions, unitptr uHost)
ARGUMENTS
- const char *pcName
- name of referenced method
- unitptr uOrig
- unit to provide interface template
- int (*Accept)(unitptr)
- - not documented in source --
- (*Notify)(unitptr
- - not documented in source --
- /* function to notify of change of used class instance */
- function to notify of change of used class instance
- const 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
/local/homes/rhaschke/nst7/man/../o.linx86_64//../nstsrc/nst_method.c