TOC PREV NEXT INDEX

Core AVS/Express and the Object Manager


A
User Code Interface (UCI) reference


This appendix provides reference material for the User Code Interface (UCI). This interface, now superseded by the Object Manager C and C++ APIs, provided a technique for encapsulation of a C or C++ structure or function in previous releases of AVS/Express, and is supported and documented here for compatibility reasons only.

This chapter discusses:

A.1 Encapsulating a C or C++ structure with the UCI

With the UCI, you can integrate a C or C++ structure (and optionally its update method) into AVS/Express, where it exists as a module. At execution time, AVS/Express maintains the connection between the structure and the module, so that an executing function can access the module's data through the structure, rather than through API routines.

A.1.1 Encapsulating a structure without an update method
Structure

Here is a C Image structure. You are responsible for providing the structure:

typedef struct _Image {
int Width, Height;
char *Data;
} Image;
Module

In AVS/Express, you define a corresponding group (you can use the module base type as well):

group Image<struct_name="Image"> {
int Width, Height;
char Data<cptr=1>[Width*Height];
};

Each subobject of the group must correspond to a similarly named member of the structure, but the order does not matter. The group's subobjects can be a subset of the structure's members. Just as in C, names are case sensitive.

Properties

The struct_name property specifies the name of the corresponding C data structure.

The cptr=1 property specifies that Data maps to a pointer.

Library

You place the group in a library, in which you set other properties.

For example, you place Image in the library User_Libraries.User_Library_1:

User_Libraries.User_Library_1
< build_dir="image",
c_hdr_files="image.h",
out_src_file="image_gen.c",
out_hdr_file="image_gen.h" > {

group Image<struct_name="Image">
{
int Width, Height;
char ImageData<cptr=1>[Width*Height];
};
};

build_dir specifies the build directory for this library. This directory should contain the image.h header file and is the location for any generated source for this library.

c_hdr_files specifies the file that contains the data structure's definition. You could provide an absolute pathname or a pathname relative to build_dir. It is better to use relative pathnames to make code easier to move in the directory structure.

out_src_file and out_hdr_file specify the name of the source and header files that UCI generates. When specified as a relative pathname, the files are created in the build directory.

Compiling a process

When you compile the group object's process (Project'Compile pull-down command), AVS/Express generates the code necessary to support encapsulation of the structure.

Data-type mappings

When creating the structure and group, you must ensure proper data-type mappings.

Follow these rules:

For details, see Section A.3.2, Table of data-type mappings  [page A-18].
A.1.2 Referencing structures within a structure
Rule

If you want the AVS/Express object to be able to use by-reference or by-pointer mode to refer to substructures, then your C structure must contain pointers to substructures. In-place substructures in C can only be used with by-value mode groups in AVS/Express.

Example 1

For example, you have a line structure that contains two point structures:

typedef struct _point {
int x;
int y;
} point;

typedef struct _line {
point pt1;
point pt2;
} line;

In AVS/Express, you define the following group objects:

group point {
int x, y;
};
group line <struct_name="line"> {
point pt1;
point pt2;
};

pt1 and pt2's reference mode must be by-value. The following, therefore, is illegal in AVS/Express:

line line1;
line line2;
line line3 {
&pt1 => line1.pt1;
&pt2 => line2.pt2;
};

This code attempts to change pt1's and pt2's reference mode to by-reference.

Example 2

Continuing the example, you change the C structure so that pt1 and pt2 are pointers to structures:


typedef struct _line {
point *pt1;
point *pt2;
} line;

You change the V definition of line to indicate that pt1 and pt2 map to pointers in the structure:

group line <struct_name="line"> {
point pt1 <cptr=1>;
point pt2 <cptr=1>;
};

The following V code is now valid:

line line3 {
&pt1 => line1.pt1;
&pt2 => line2.pt2;
};
A.1.3 Encapsulating a structure with an update method

A structure can have an associated update method; i.e., a function that executes whenever the structure's data changes. The function typically determines which data has changed, then performs any required processing.

In the module corresponding to the structure, you include a cmethod object that refers to the target update function to be called whenever the module's data changes.

Structure

Here is a C Image structure:

typedef struct _Image
{
int Width, Height;
char *Data;
} Image;
Object

You create a corresponding Image object and, in this case, place it in User_Libries.User_Library_1. Notice that the module has a cmethod object:

User_Libraries.User_Library_1
< build_dir="image",
c_hdr_files="image.h",
out_src_file="image_gen.c",
out_hdr_file="image_gen.h" > {

module Image<struct_name="Image">
{
int Width, Height;
char Data<cptr=1>[Width*Height];
cmethod+notify+req upd
<src_file="imupd.c"> = "ImageUpdate";
};
};

cmethod indicates that this function uses the UCI. The src_file property indicates the target function's source file. The src_file property does not generate prototype source code for UCI methods. (You can also use the c_src_files property but can then not use the Edit Source feature in the Network Editor.) The target function's name is ImageUpdate.

Function

The target function, when invoked at execution time, receives two arguments: a pointer to the data structure and a pointer to a UCI-generated bitmask structure. The bitmask structure indicates which members in the data structure have changed (see more details, below).

Here is the prototype of the function you should provide:

int function_name (data_struct *, bitmask_struct *)

Here is a portion of a ImageUpdate, the update function:

/*
 * The UCI-generated header file. Note that you do NOT
 * have to include the data structure's header file.
 * It is already included in the UCI-generated header file.
 */
#include "image_gen.h"

int
ImageUpdate(Image *img, Image_bitmask *Bitmask)
{
/*
* Test whether width has changed. If so, call
 * a routine to process the change. Pass the image's
 * width and data.
*/
if(Bitmask->Width)
Process_width(img->Width, img->Data);
...
Bitmask structure

The UCI generates and maintains a bitmask structure that indicates which data in the structure has changed. The bitmask structure has a bit-field member for each of the object's data subobjects. A bit set to 1 means that the corresponding data subobject has been changed.

Here is the bitmask structure for the Image object:

/*
 * Bit-mask structure, created by the UCI.
 */
typedef struct _Image_bitmask {
unsigned int Height:1;
unsigned int Width:1;
unsigned int Data:1;
} Image_bitmask;

The structure's default name is the name of the data structure with the suffix _bitmask added. You can specify a different name with the struct_bitmask property.

If a subobject is itself an object that maps to a structure, the UCI generates an embedded bit-mask structure for the subobject.

If the module maps to a pointer to a structure, or to a pointer to a list of pointers to structures, the UCI generates a single bit field for the pointer.

A.1.4 Resolving structure pointers
Purpose

The update function may need to resolve pointers to structures. If it does, it calls the API routine OMcstruct_resolve_ptr.

Here are two situations where a function needs to resolve pointers:

See the example below.
Example

For example, say the Image object has an additional subobject, a module object called parentImage:

module Image< struct_name = "Image" > {
int Height;
int Width;
char ImageData<cptr=1>[Width*Height];
module &parentImage <cptr=1>;
cmethod+notify+req  update = "ImageUpdate";
};

Image's C data structure now looks like this:

typedef struct _Image {
int Height;
int Width;
char *Data;
Image *parentImage;
} Image;

ImageUpdate must call OMcstruct_resolve_ptr before accessing parentImage's data:

...
OMcstruct_resolve_ptr(parentImage,NULL);
OMcstruct_resolve_ptr is not recursive

OMcstruct_resolve_ptr is not recursive. It resolves only the structure pointer it is given. It does not resolve any embedded structure pointers. A function must call OMcstruct_resolve_ptr for each structure pointer.

No bitmask structure

The resolved structure does not have a corresponding bitmask structure to indicate which data changed. There is only the single bit in the structure that contains the pointer that indicates whether or not any data in the resolved structure has changed.

A.1.5 Setting an object's data
Description

A function can set its object's data. It does this by modifying the C data structure, setting a bitmask structure to indicate which data has changed, then calling OMcstruct_update_om.

Example

For example, assume that ImageUpdate modifies the image's data. Here is what the function now looks like:

/*
 * Generated header file for the bitmask structure. Includes
 * the data structure's header file.
 */
#include "image_gen.h"

void
ImageUpdate (Image *img, Image_bitmask *Bitmask)
{
/*
 * Create NewBitmask to identify the changed data.
 */
Image_bitmask NewBitmask;
/* Zero out the bitmask with this C library routine */
memset(&NewBitmask, 0, sizeof(WidgetData_Bitmask));
/*
 * Test whether width has changed. If so, call
 * a routine to process the change. Pass the image's width
 * and data. Assume that data is changed.
 */
if(Bitmask->width) {
Process_width(img->width, img->data);
NewBitmask.data = 1;  /* Set NewBitmask. */
};
...
/*
 * Call OMcstruct_update_om to update the network.
* You MUST do this or no data in your module will change
 */
OMcstruct_update_om(img, &NewBitmask);
};
A.2 Encapsulating an existing C or C++ function with the UCI

With the UCI, you can integrate a C or C++ function into AVS/Express, where it exists as a module object. AVS/Express generates wrapper code so that at execution time, when the function needs to be called, it is passed the arguments it expects. The function does not have to be modified at all.

A.2.1 Calling a C function directly from the UCI
C function

For example, you want to be able to call the existing C function ave as is, without having to modify it in any way, such as with API calls. The function returns the average of its two input arguments:

float ave(float num1, float num2)
{
float average;
average = (num1 + num2) / 2;
return average;
};
Object

In AVS/Express, you create a module, as shown below. The object includes several properties that supply information that allows the UCI to generate the necessary wrapper code. You include it in the library User_Libraries.User_Library_1, which was used to store Image objects in the examples earlier in this chapter:

User_Libraries.User_Library_1
< build_dir="image",
c_hdr_files="image.h",
out_src_file="image_gen.c",
out_hdr_file="image_gen.h" > {

module ave
<struct_name="aveStruct",
 cfunction="result=ave(num1, num2)",
 src_file="ave.c" >
{
float+Iparam &num1;
float+Iparam &num2;
float+Oparam result <modified = 1>;
cmethod+notify+req upd ="aveFunc";
};
struct_name

The UCI generates a data structure and a bitmask, as part of its wrapper code. With the struct_name property, you specify a name for the structure. The convention is to specify the function name with the suffix Struct.

cfunction

The cfunction property indicates how the target function should be called. num1 is the first parameter. num2 is the second parameter. result receives the returned value. The property also indicates the name of the function, in this case, ave.

modified = 1
modified = 2

You specify the modified = 1 property for each data subobject that will be modified by the function.

It is possible for an object to be modified in a way that is not visible to the Object Manager. For example, the object may be a pointer to some data that the function changes. If you want the Object Manager to treat the object as if it had changed-for example, for the purposes of sending a notification to some method-you specify the modified = 2 property.

aveFunc as the target function

The UCI generates a wrapper function that is responsible for calling function ave with the expected parameters, and for updating module ave as expected when the function returns. In the cmethod object, you specify the name of the wrapper function that UCI should generate. The convention is to specify the function name with the suffix Func.

A.2.2 What actually happens

When you compile a process, the UCI generates a data structure (and a bitmask structure) to which the module's data objects map. It also generates a wrapper function.

At execution time, when your function needs to be called, the Object Manager instead sets the values of the UCI-generate data and bitmask structures, then calls the UCI-generated update function. The wrapper function then calls your function with the expected parameters and updates the AVS/Express object as necessary when the function returns.

A.2.3 Data mappings and pointers

The mapping between the AVS/Express object and the function you specify with the cfunction property takes place via data structures that the UCI generates. Often you don't care how the mapping takes place but sometimes the mapping makes a difference.

cptr=1/cptr_list = 1

The cptr and cptr_list properties determine how the UCI builds the data structure's members:

int num1;
then the UCI creates the structure member
int num1;
int num1 <cptr=1>;
then the UCI creates the structure member
int *num1;
cfunction

The cfunction property tells the UCI how the data in the structure then maps to the target function.

For example:

cfunction="result=ave(num1, num2)"

specifies that the value of num1 and the value of num2, as stored in the data structure, are passed to the target function.

and:

cfunction="result=ave(&num1, &num2)"

specifies that a pointer to num1 and a pointer to num2, as stored in the data structure, are passed to the target function.

Passing a pointer

Generally, then, you have two ways to pass a pointer to your function.

For example, assume that for some reason your function took a pointer to num1. Here is the function's prototype:

float ave(float *num1, float num2);

In the module object, you must indicate that a pointer needs to be passed to your function. Here is one way:

int num1 <cptr=1>;

This stores a pointer to num1 in the structure. The pointer is then passed to the function.

Here is another way:

cfunction="result=ave(&num1, num2)"

This passes to your target function a pointer to num1 in the data structure.

Pointers and group subobjects

In certain situations, when you want to pass a pointer to a group parameter, you must indicate a pointer with the cptr=1 property and not the cfunction property.

See Section A.2.4, Image example  [page A-14].
A.2.4 Image example

You can include a UCI-encapsulated structure in the argument to a function.

C function

Consider the Image structure that was encapsulated in the examples earlier in this chapter. The example Image library has several functions that operate on images. Here is the prototype of one of them, EdgeDetect:

void EdgeDetect(Image *, Image *, int);

The first argument is a pointer to an input Image; the second, a pointer to an output Image; and the third, an input parameter indicating the edge-detection type.

Objects

Here is the V code for the EdgeDetect object you create in AVS/Express, including its enclosing library:

User_Libraries.User_Library_1
< build_dir="image",
c_hdr_files="image.h",
out_src_file="image_gen.c",
out_hdr_file="image_gen.h" > {

module EdgeDetect
<struct_name="EdgeDetectStruct",
 cfunction="EdgeDetect(In, &Out, Type)",
 src_file="EdgeDetect.c" >
{
Image+Iparam &In <cptr=1>;
Image+Oparam Out <modified = 1> {
Width => In.Width;
Height => In.Height;
};
int Type;
cmethod+notify+req upd ="EdgeDetectFunc";
};
Notice that In specifies cptr=1

Subobject In is a module object that you want to pass to EdgeDetect through a pointer. In this case, you must use the cptr=1 property, and not the cfunction property, to request a pointer.

In the UCI's generated data structure, In becomes a structure within that structure.

The UCI has a rule that a substructure's reference mode in the AVS/Express object must be by-value. It cannot be by-reference or by-pointer.

In's reference mode is by-reference (you will connect it to some other object), so you cannot store it in the generated structure as a structure. You must store it as a pointer to a structure. And that is what happens when you specify the cptr=1 property.

A.3 UCI reference

This section is a reference to UCI properties and data-type mappings.

A.3.1 UCI properties
Properties on a module

The following UCI properties can be applied to a module:

Name

Type
(and default value)
Description

struct_name
string ("my_struct")
The name of the module's corresponding C data structure. If you are not using the cfunction property, you supply this structure in a header file named in the c_hdr_files property. If using the cfunction property, AVS/Express generates the structure with the name supplied in the struct_name property.
struct_bitmask
string ("my_bitmask")
The name the UCI should give to the bit-mask structure it creates. The bit-mask structure is part of the interface for calling an update function. By default, the name is <struct_name>_bitmask.
constructor_func
string
("my_constructor")
The name of a constructor function to be called when the C structure is allocated. By default, the structure is allocated with a simple call to malloc(), and memory is set to zeros.
A constructor function, if provided, should have the following prototype, where the argument passed to the function is the id of the module:
struct_name *constructor_func_name (OMobj_id obj);
Where obj is the OMobj_id of the module being allocated.
See also:
descructor_func
destructor_func
string
("my_constructor")
The name of a destructor function for the C structure. By default, the structure is deallocated with a simple call to free.
A destructor function, if provided, should have the following prototype, where the argument passed to the function is a pointer to the data structure:
void destructor_func_name (struct_name *);
cfunction
string
("res = sin(x)")
A string indicating the function's name and how the function will be called. This applies only when encapsulating existing functions. This example uses the C library routine pow:
cfunction = "res=pow(arg, exp)"

Properties on a parameter

The following UCI properties can be applied to a parameter of a module:

Name

Type
(and default value)
Description

cptr
Boolean (0)
If set, indicates that the parameter maps to a pointer.
cptr_list
Boolean (0)
If set, indicates that the parameter maps to a pointer to an array of pointers.
modified
int (0)
If set to 1, indicates that the parameter is modified by the function.
If set to 2, indicates that the parameter is modified by the function, but in a way that is not visible to the Object Manager. The OM should treat the object as if it has been changed.
In either case, this property applies only when encapsulating functions.
unmanaged
Boolean (0)
Indicates that the parameter does not have a mapping in the structure. Use this if you have parameters in the module that are not represented in the C structure.

Properties on a library

The following code management properties typically apply either to a module or, more likely, to a module's enclosing library:

Name

Type
(and default value)
Description

c_hdr_files
string ("my_hdr1.h
my_hdr2.h")
The header file containing the structure's definition and any header files that this header file depends upon. The filenames you specify are relative to the directories specified by hdr_dirs.
hdr_dirs
string ("include .")
A list of directories to search for header files (equivalent to a compiler's -I option). A pathname can be absolute or relative to the build directory (build_dir property).
out_src_file
out_hdr_file
string (express.c)
string (express.h)
The filenames UCI should give to the source and header files it generates. You can specify an absolute pathname or a pathname relative to the build directory. Absolute pathnames make moving projects difficult.

A.3.2 Table of data-type mappings

The following table describes the permissible data-type mappings between a module subobject and its corresponding member in a C structure. Notes are in parentheses:

Data type of
module subobject
Corresponding
C data type (1)
Corresponding C
pointer (<cptr=1>)
Corresponding C pointer to array of pointers (<cptr_list=1>)
prim              (2)
prim[c]          (2, 3)
prim[c][c]...   (2, 3)
prim[ ]          (2, 4)
prim[c][ ]      (2, 3)
prim             (2)
prim[c]         (2)
prim[c][c]...  (2)
--
--
--
prim *          (2)
prim *           (2)
prim *           (2)
prim *           (2)
--
--
--
--
--
string           (5)
string[c]       (3, 5)
string[ ]        (3, 5)
char *
char (*)[c]    (3)
--
--
--
--
--
--
char **
module        
module[c]
module[ ]
struct           (6, 7)
struct[c]      (3, 6, 8)
--
struct *          (9)
struct(*)[c]    (3, 9)
--
--
struct **     (9)
struct **     (9)

Notes
1. Arrays in this column are somewhat inefficient.
2. prim can be char, byte, short, int, float, or double. In AVS/Express, a prim's mode can be value, reference (&), or pointer (*).
3. [c] represents a constant-sized array dimension.
4. [ ] represents an unspecified array dimension.
5. string also applies to AVS/Express type pointer. The mode can be value, reference (&), or pointer (*).
6. For this mapping, the mode of the module must be value. It cannot be reference or pointer.
7. A sub-bit-mask structure is added to the bit-mask structure.
8. An array of sub-bit-mask structures is added to the bit-mask structure.
9. The function OMcstruct_resolve_ptr must be used before dereferencing pointers for structures. In the case of a pointer to a list of pointers, the first pointer is valid, but OMcstruct_resolve_ptr must be used before dereferencing any of the pointers in the list.


TOC PREV NEXT INDEX

Copyright © 2001 Advanced Visual Systems Inc.
All rights reserved.