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