NAME
nst_typedef -- extend the set of built-in NST pin data types
PROTOTYPE
void nst_typedef( char *pcName, int (*pCompatible)(void*,void*), void* (*pCopy)(void*,void*), void (*pFree)(void*))
ARGUMENTS
- char *pcName
- name of the new user-specified data type
- int (*pCompatible)(void*,void*)
- function to determine compatibility of two instances of the user-specified data type (see below)
- void* (*pCopy)(void*,void*)
- copy method for user-specified data type. Must admit argument values NULL (expected to provide `default instance'' of user-specified data type.
- void (*pFree)(void*)
- destructor function for the user-specified data type
SYNOPSIS:
The nst_typedef() function allows to extend the set of built-in
NST pin data types by new, user-specified data types. A user-specified
data type is identified by a print name pcName and by three
methods pCompatible(.,.), pCopy(.,.) and pFree(.) that provide
some basic operations for the data type. The nst_typedef() function
call makes this information known to NST. Subsequently, the
NST_SPECIAL keyword, followed by the print name pcName
of the data type, can be used in the primitive_unit and
void_unit constructor functions to make units that have pins
of the new data type. A call nst_typedef(name,NULL,NULL,NULL)
undefines a previously defined data type name. After that,
no further operations on pins of that type (including
removal) may occur.
DESCRIPTION:
NST offers a special pin type NST_SPECIAL to pass arbitrary,
user-specified data types via NST input or output pins. A pin
of type NST_SPECIAL is always associated with a particular,
user-specified data type, and only NST_SPECIAL pins that are
associated with the same user-specified data type can be connected
by a NST-``wire''.
Before NST can create or manage any
NST_SPECIAL pins of a particular, user-specified data type,
it needs some basic information about the new data type.
This information has to be provided by a prior call of
the nst_typedef() function.
The first argument pcName specifies a print name by which the
data type will be identified in the future. The remaining
arguments specify some basic methods that must be provided
for handling the user-specified data type. These methods are the
following three user-provided functions ( p,p1,p2 denote
pointers to instances of the user-specified data type):
- void *pCopy(void *p1, void *p2)
- copy of an
instance *p2 of the user specified data type,
allocating the necessary memory if p1=NULL, or using
the memory of an existing instance *p1 to minimize the
number of necessary realloc() and free() operations
(afterwards, *p1 is expected to be replaced by the copy).
If a non-NULL p1 is passed, ownership of the instance
is passed to the routine pCopy.
The returned address should be the address of the newly
created copy (ownership of that copy is assumed to pass
to the caller of pCopy). The call pCopy(NULL,NULL) must be legal
and, if returning non-NULL, will be assumed to yield the address
of a newly created default object that will be used to initialize
the value of a pin of the user-specified data type. For
pCopy(NULL,NULL)=NULL, initialization will not be
automatic and must instead be carried out explicitly
before a newly created pin may be used.
Implementation note:
The efficiency of the provided pCopy() function translates
directly into the efficiency of many NST-operations on the user-specified
data type, since it is used whenever NST needs to transfer
an instance of the data type to another place (e.g., the
use_named units need to do this). Ideally, if instances
*p1 and *p2 are very similar, pCopy should be able to
recognize this and carry out as few operations as possible
(this may be facilitated by a suitable design of the user-specified
data type). Also, if *p1 is not NULL, pCopy should try
hard to deliver the copied instance at the adress p1 (which
can always be ensured by wrapping a data structure with a
struct that holds only an indirection pointer which then
has no further constraint). The simple strategy of freeing
*p1 and then allocating a new copy of *p2 works, but
usually is only a poor implementation!
- void *pFree(void *p)
- free an instance of the user
specified data type that is at address p. pFree() should
follow the semantics of free() and allow a NULL argument
(doing nothing in this case). An argument
value of NULL instead of the function pointer pFree
is equivalent to choosing the function free().
- int pCompatible(void *p1, void *p2)
-
return 1, if two instances *p1, *p2 of the user
specified data type are compatible, 0 else.
In particular, pCompatible(p2,p1)=1 has to imply that
pCopy(p1,p2) is a legal function call.
The pCompatible() function must be provided to specify
additional restrictions for connections among pins of the
same data type. For instance, if a user-specified data type
implements variable-length vectors, a desired restriction
might be to allow only connections between pins of vectors
of the same current length. An argument value of NULL
instead of the function pointer pCompatible sets the
constant function 1 (i.e., no restrictions beyond having
the same user-specified type).
After a user-specified data type has been defined with
the nst_typedef() function it is globally visible and
permanently bound
to the chosen print name, i.e., a redefinition with changed
values of pCopy, pFree or pCompatible will result in
an error message (any `re''definition with the same values
will be accepted silently). This ensures that there can
be no conflicting definitions from different modules.
NST_TYPEDEF Macro:
The macro
NST_TYPEDEF(pcName,pCompatible,pCopy,pFree)
provides a convenience wrapper of the nst_typedef()
function with the necessary casts to allow its
function arguments to have parameter and result
pointer types different from the generic void*
pointer type that is required by the
nst_typedef() function.
CREATING UNITS WITH USER-DEFINED DATA TYPE PINS:
Pins for user-specified data types are created in a similar way as the
predefined pin types. To make a pin for the user-specified
data type with print name mytype, either use the type keyword
NST_SPECIAL, followed by the print name mytype as the next
parameter (instead of a dimension) in the parameter list
of the primitive_unit() or void_unit() constructor functions,
or, alternatively, specify the type printname in angle brackets
in the proper pin position of the field format strings passed
to the primitive_unit3 and void_unit3() constructor functions.
EXAMPLES:
NST_TYPEDEF("list",pListCompatbl,pListCpy,pListFree);
NST_TYPEDEF("MyType",pMyTypeCompatbl,pMyTypeCpy,pMyTypeFree);
u = primitive_unit(
NST_INP, /* input field 0 */
NST_SCALAR, 2, /* two scalar pins */
NST_SPECIAL, "list", /* one list pin */
NST_SCALAR, 2, /* two more scalar pins */
NST_INP, /* input field 1 */
NST_SPECIAL, "list", /* a single list pin */
NST_OUT, /* output field 0 */
NST_SPECIAL, "MyType", /* a Mytype-pin */
NST_FLOAT, 10, /* 10-dim float vector */
NST_HOST, uHost);
will create a unit with two input fields and one output field.
The first input field has 5 pins: two scalar pins,
one pin of the user-specified data type with print name "list",
and two further scalar pins. The second input field has a
single pin of type "list".
The output field has two pins, the first being a pin
of the user-specified type with print name "MyType", and
the second a float vector of 10 elements. A unit with the
same interface can be created with
char *apcInp[]={"s2<list>s2","<list>"};
char *apcOut[]={"<MyType>f10"};
NST_TYPEDEF("list",pListCompatbl,pListCpy,pListFree);
NST_TYPEDEF("MyType",pMyTypeCompatbl,pMyTypeCpy,pMyTypeFree);
u = primitive_unit3(2,apcInp,1,apcOut,uHost);
In both cases, the NST_SPECIAL pins should be initialized with
suitable object instances. If this is not already done
via the specified pCopy() functions,
the necessary initialization must follow the construction
of u and might for the present example look like this:
/* initialize SPECIAL pins with suitable objects: */
pvNstInp(u,0,1) = create_list_instance(..);
pvNstInp(u,1,0) = create_list_instance(..);
pvNstOut(u,0,0) = create_MyType_instance(..);
Since this allows to make the initializer objects depend
on specific parameters (..), this offers more flexibility
than using the the pCopy(NULL,NULL) value for automatic
intialization (to suppress automatic initialization via the
pCopy function requires that pCopy(NULL,NULL)=NULL).
USING USER SPECIFIED DATA TYPE PINS:
The macros pvNstInp(u,f,i) and pvNstOut(u,f,i) provide the
address (cast to void*) of the user-specified data structure
that is held in pin i of field f of unit u. Access of any
elements or any alterations to the data structure are entirely
in the responsibility of the user (Important: the address of
the data structure must not be changed!).
NST will take care of the creation (in the default configuration
returned by pCopy(p,NULL)), destruction and
connection of pins for user-specified data types.
If pCopy(p,NULL)=NULL, NST will leave the responsibility to
allocate an initial value for user-specified data structures
to the user-written constructor function.
The routine nst_typename_of(pin *p) returns a pointer to
the print name that was specified for the user-specified
type of the pin p, or NULL, if no such definition was
made.
VOID PINS:
A closely related NST data type is the type NST_VOID.
Its main purpose is analogous to the void* pointer type
of C. In NST, it provides a flexible pin type for
passing NST_SPECIAL values through containers
without having to worry about the actual user-specified
type.
Pins of type NST_VOID can be connected either among themselves
or with pins of type NST_SPECIAL. They have no fixed
user-specified data type.
Instead, they flexibly `borrow'' their current data type from
any NST_SPECIAL pins they are currently connected with. Their
main use is in the interface of container units to
allow access to inside NST_SPECIAL
pins without having to know the user-specified type beforehand.
The `borrowed'' type of a NST_VOID pin immediately propagates
to any other connected NST_VOID pins. As a result, any
connected set of NST_VOID pins always shares the single
user-specified type that pertains to the NST_SPECIAL pin
that was connected first (further NST_SPECIAL pins can only be
attached if they have the same user-specified type as
the first NST_SPECIAL pin from which the attached VOID
pins already have borrowed their type). The NST_VOID
pins loose their borrowed type when the last connection
to a NST_SPECIAL pin is cut.
EXAMPLES:
The file nst_list.c provides an implementation of
a user-specified data type "list" for accommodating
singly linked lists of data records that can be any
combination of the standard predefined NST data types.
Therefore, the implemented "list" data type is actually
a class of data types (differing by the record structure of
their list elements) and nst_list.c illustrates
how such a class can be implemented.
SEE ALSO:
void_unit, primitive_unit, container_unit
FILE
/local/homes/rhaschke/nst7/man/../o.linx86//../nstsrc/nst9.c