TOC PREV NEXT INDEX

Using AVS/Express




9 Object Manager APIs


This chapter discusses the Object Manager APIs, a set of language-specific APIs that allow C, C++, and FORTRAN code to communicate with the Object Manager. It includes the following sections:

9.1 Introduction

When you include a method in a module, you must also provide code that implements the method. This code frequently must communicate with the Object Manager to obtain or supply information, hence AVS/Express provides a set of language-specific Object Manager APIs. Object Manager APIs are available in C, C++, and FORTRAN, to support method implementations as C functions, C++ classes, and FORTRAN subroutines. Each API contains a set of routines that accomplish various tasks required by your code.

The C API is a special API: it serves not only as the interface between your C functions and the the Object Manager, but as the primary interface between the most AVS/Express objects and the Object Manager. As a result, communications between C functions and the Object Manager are handled differently than communications between C++ classes or FORTRAN subroutines and the Object Manager.

The following figure illustrates this structure.

Figure I-1


The following sections describe the Object Manager APIs. Because the C and C++ APIs have much in common, they are discussed together, with separate subsections discussing specific topics as appropriate. The FORTRAN API is discussed separately.

9.2 The C and C++ APIs: Overview

The C and C++ APIs include five types of routines, identifiable by their prefixes as noted in the following table:

Table I-1
Type
C prefix
C++ prefix
Array allocation/deallocation routines
ARR
ARR
Error handling routines
ERR
ERR
Event handling routines
EV
EV
Object Manager manipulation routines
OM
OMX
Worldwide language routines
TM
TM

The AVS/Express online help system provides reference descriptions of the C and C++ API routines. Use these descriptions as your primary source of information (see the next section for notes on how to use the descriptions). To supplement these descriptions, this section provides a summary of the available routines and information on header files, and subsequent sections provide usage notes for the C and C++ API routines.

Using the Online Help System Descriptions

For conciseness, the online help system does not provide separate descriptions of C and C++ API routines; instead, it provides a single description for both routines, listed under the name of the C routine. For example, the description of OMfind_obj applies both to OMfind_obj (a C API routine) and find_obj (a C++ API routine).

Also for conciseness, the AVS/Express online help system provides a single description for related routines rather than a separate description for each routine. For example, the description of OMget_data_type also applies to OMset_data_type. Specifically:

The organization of the summary of routines, later in this section, reflects these groupings.

First (Object) Argument Changes

With one exception, the parameters, return code, and functionality for a C++ API routine are the same as those for the C API routine with which it is documented. The exception is that the first argument to the C API routine is replaced by the object on which the C++ API routine is invoked. For example, consider the following C API call:

OMobj_id obj_id = ...
OMset_int_val(obj_id, 3);

This is the C++ API equivalent:

OMXobj *obj = ...
obj->set_int_val(3);
Defaulted C++ Arguments

Some C++ methods have defaulted arguments. If you do not supply a value for these arguments, the default value is supplied. In the online help, defaulted arguments are indicated in the declaration of the routine. For example:

void *OMXobj::ret_array_ptr(
int mode,
int *size = NULL,
int *type = NULL);

In this definition, you can call this routine as follows:

void *my_ptr = my_obj->ret_array_ptr(OM_GET_ARRAY_RD);

In this case, AVS/Express supplies NULL values for the size and type arguments.

C and C++ API Routines

This section summarizes the C and C++ API routines. As in the online help system, it lists only the C API routines; to obtain the name of the C++ routine that corresponds to a C API routine, simply remove the OM prefix from the C API routine name.

The tables in this section group the routines by functional category, to help you find the routine you need to accomplish a particular task. Within functional groupings, the routines are grouped to reflect the organization of the C/C++ descriptions in the online help system. (For details, see Using the Online Help System Descriptions on page 9-4.)

Note: When two or more API routines appear together, see the description of the first routine listed for details on all routines in the group.
Getting and Comparing Object IDs and Names
Table I-2
Routine
Description
OMfind_subobj
OMlookup_subobj
OMfind_obj
Returns the ID for an specified object
OMget_array_subobj
Treats a specified object's set of subobjects as an array and returns the ID of a specified subobject
OMget_num_subobjs
Gets the number of subobjects for a specified parent object
OMget_array_val
OMset_array_val
OMdel_array_val
Gets, sets, or deletes an object ID in a one-dimensional array object (the object is either an array of pointers/references to other objects or a by-value group array)
OMret_obj_parent
OMget_obj_parent
Returns or gets the ID of an object's parent
OMequal_objs
Compares two object IDs
OMis_null_obj
Determines whether an object ID is null
OMname_to_str
OMstr_to_name
Converts a OMobj_name to a string, and vice versa
OMfind_str_subobj
Returns the ID of an object's subobject, specified as a path

Creating and Deleting Objects
Table I-3
Routine
Description
OMcopy_obj
Creates an object from a specified class object
OMcreate_obj
Creates an object from a specified primitive type
OMcreate_obj_from_path
Creates an object from a specified class or primitive type
OMdestroy_obj
Decrements an object's reference count, deleting the object if the reference count is 0
OMuser_destroy_obj
Deletes all references to an object, then deletes the object
OMget_uniq_name
Gets an object name that is unique among a specified object's subobjects
OMadd_subobj
Makes one object the subobject of another
OMdel_subobj
Deletes an object from its parent
OMinsert_subobj
Makes one object an immediate subobject of another
OMadd_to_refcnt
Add to reference count of an object

Getting and Setting Object Definitions
Table I-4
Routine
Description
OMget_data_type
OMset_data_type
Gets or sets an object's data type
OMget_obj_att
OMset_obj_att
Gets or sets an attribute of an object
OMget_obj_name
OMset_obj_name
OMret_obj_name
Gets, sets, or returns an object's name
OMget_cur_seq
Gets the current sequence number
OMget_obj_seq
Gets a particular object's sequence number
OMget_obj_prop
OMset_obj_prop
OMget_obj_iprop
OMset_obj_iprop
OMget_obj_rprop
OMset_obj_rprop
OMget_obj_sprop
OMset_obj_sprop
OMset_obj_iarray_prop
Gets or sets a property of an object
OMget_obj_ref_mode
OMset_obj_ref_mode
Gets or sets an object's reference mode
OMret_typed_array_ptr
Returns an object's name as a character string
OMret_obj_path
Returns an object's full pathname as a character string
OMret_obj_path_to
Returns an object's partial pathname as a character string
OMobj_is_type_of
Tests an object's base type
OMstr_to_type
Returns the base type ID corresponding to a type name

Getting and Setting Object Connections
Table I-5
Routine
Description
OMadd_obj_ref
Connects one object to another (useful for connecting to an array of objects)
OMinsert_obj_ref
Inserts a new connection into an existing list of connections
OMset_obj_ref
Connects one object to another (useful for connecting to a single object)
OMdel_obj_ref
Disconnects one object from another
OMget_array_ref
Gets a list of connections from an object
OMget_obj_ref_mode
OMset_obj_ref_mode
Gets or sets a single connection from an object
OMget_num_refs
Gets the number of connections for an object
OMlink_objs
Connects one object to another using an intermediate name-link object
OMget_ref_to_list
Gets the connections from an object
OMget_port_list
Gets the list of ports for an object

Getting and Setting Scalar Object Values
Table I-6
Routine
Description
OMget_obj_val
OMget_obj_pval
OMset_obj_val
Gets or sets an object's value (used when an object's mode is by-reference or by-pointer, to determine the pointed-to object)
OMget_int_val
OMset_int_val
OMget_name_int_val
OMset_name_int_val
Gets or sets an integer object's value
OMget_ptr_val
OMset_ptr_val
OMget_name_ptr_val
OMset_name_ptr_val
Gets or sets a pointer object's value
OMget_real_val
OMset_real_val
OMget_name_real_val
OMset_name_real_val
Gets or sets a floating-point object's value
OMret_str_val
OMget_str_val
OMset_str_val
OMret_name_str_val
OMget_name_str_val
OMset_name_str_val
Gets, sets, or returns a string object's value

Getting and Setting Array Object Values
Table I-7
Routine
Description
OMret_array_ptr
OMret_name_array_ptr
OMret_typed_array_ptr
Returns the value of an array (used for getting and setting an array object's value)
OMget_array
Gets or sets the value of an array
ARRalloc
Allocates space for an array object
ARRfree
Frees an array object's space
ARRincr_refcnt
Increments the reference count on an array allocated with ARRalloc
OMget_array_dims
Gets an array object's dimensions
OMget_array_ref
Gets an array object's list of connections
OMget_array_size
OMset_array_size
Gets or sets a one-dimensional array object's size
OMret_str_array_val
OMget_str_array_val
OMset_str_array_val
Gets, sets, or returns an element in a one-dimensional string-array object
OMget_sub_barray
OMset_sub_barray
OMget_sub_sarray
OMset_sub_sarray
OMget_sub_iarray
OMset_sub_iarray
OMget_sub_farray
OMset_sub_farray
OMget_sub_rarray
OMset_sub_rarray
Gets or sets a subarray of an array object
OMset_array
Sets an array object (that is, all elements in the array object)

Method Utility Routines
Table I-8
Routine
Description
OMchanged
Indicates whether or not a parameter has changed
OMstatus_check
Updates method's percent done, running module and allows interrupt
OMpush_status_range
OMpop_status_range
Allows reuse of code that uses OMstatus_check

Controlling Method Execution Order
Table I-9
Routine
Description
OMadd_notify_req
OMdel_notify_req
Adds or deletes a notification request
OMpush_ctx
OMpop_ctx
Defines a push/pop context
OMbegin_op
OMend_op
Suspends and resumes object execution
EVadd_select
EVdel_select
Adds or deletes an event callback handler
EVXinit_display
EVXadd_event_hndlr
EVXdel_event_hndlr
EVXdel_events
Defines callback handlers for X events
OMset_state_mode
Sets the state mode in a context
EVflush_events
Delivers any pending expose events
OMchanged
Sets an object's instanced flag so it can execute

Debugging Applications
Table I-10
Routine
Description
OMedit_obj
OMedit_root_obj
OMedit_obj_ptr
Brings up the VCP on an object
OMprint_obj
OMprint_obj_ptr
Prints an object's definition
OMprint_notifies
Prints the notifications on an object
OMprint_path
OMprint_path_ptr
Prints an object's path
OMset_verbose
Sets the verbose mode for a set of events

Managing Processes
Table I-11
Routine
Description
OMadd_exit_func
Adds a function to be called when the process exits
OMis_main_process
Determines whether the calling process is the scheduler
OMmain_loop
Starts the main loop for dispatching events
OMobj_proc
Returns an object's process ID
OMexit
Exits cleanly from the currently running process
OMprocess_exit
Causes the specified process to exit (can be used to get the main process to exit from an external process)
OMmain_init
Initializes the Object Manager
OMget_path
Returns value of XP_PATH

Saving and Restoring Object Descriptions
Table I-12
Routine
Description
OMparse_buffer
Parses V statements stored in a character string
OMparse_obj_val
OMparse_obj_ref
Parses the value of an object using V
OMparse_stream
Reads and parses a stream of V statements
OMread_desc
Reads an ascii V file into AVS/Express
OMopen_file
Reads an ascii or binary V file into AVS/Express
OMwrite_obj
Saves an object to a a text V file

Mapping Method Objects to Functions
Table I-13
Routine
Description
OMadd_named_func
Adds a single entry to AVS/Express' function-identifier table
OMadd_named_funcs
Adds several entries to AVS/Express' function-identifier table
OMfunc_to_name
Returns the identifier corresponding to a function
OMname_to_func
Returns the function corresponding to an identifier
OMget_func_val
OMset_func_val
Gets or sets a method object's function pointer

Methods on C++ API Objects
Table I-14
C++ method
Description
OMXobj::OMXobj
Constructor for creating a new AVS/Express object
OMXgroup::ret_omx_ptr
Implements the cast operation for AVS/Express C++ objects
OMret_omx_ptr
Returns a pointer to a C++ class corresponding to an AVS/Express object
OMXgroup::ret_class_ptr
Returns a pointer to a user-defined C++ class associated with an AVS/Express object

Table I-15
For C++ method...
See C API routine..
Description
OMXobj::add_obj_ref
OMadd_obj_ref
Adds a connection to an object
OMXobj::del_obj_ref
OMdel_obj_ref
Deletes a connection from an object
OMXobj::set_obj_ref
OMset_obj_ref
Sets the connection for an object
OMXobj::get_array
OMget_array
Gets the value of an array (for reading and/or writing)
OMXobj::set_array
OMset_array
Set the value of an array
OMXobj::get_array_size
OMget_array_size
OMset_array_size
Gets the size of a one-dimensional array
OMXobj::get_int_val
OMXobj::set_int_val
OMget_int_val
OMset_int_val
Gets and sets an integer value
OMXobj::get_real_val
OMXobj::set_real_val
OMget_real_val
OMset_real_val
Gets and sets a real value
OMXobj::ret_array_ptr
OMret_array_ptr
Returns the pointer to an array type
OMXobj::
ret_typed_array_ptr
OMret_typed_array_ptr
Returns the pointer to an array object and forces the type to a supplied type
OMXobj::changed
OMchanged
Indicates whether or not a parameter has changed
OMXobj::push_ctx
OMXobj::pop_ctx
OMpush_ctx
OMpop_ctx
Controls method execution

Error Handling
Table I-16
Routine
Description
ERRsquash_start
ERRsquash_end
Suppresses errors generated by API routines

Worldwide Language Support
Table I-17
Routine
Description
TMload_dictionary
Loads a dictionary file
TMget_translation
Translates a string

C and C++ API Header Files

When you implement a method with code that calls C API routines to communicate with the Object Manager, you must include various header files that define items that the Object Manager uses. You may need to include some or all of the following header files:

#include <avs/om.h>
However, if it accesses the Object Manager via the C++ API, you include the basic C++ Object Manager header file instead:
#include <avs/omx.hxx>
If your code uses the definition of any attributes or properties, you also include the attributes header file:
#include <avs/om_att.h>
If it manipulates any base types, you also include the type header file:
#include <avs/om_type.h>
#include <avs/event.h>
9.3 C API Usage Notes

The C API is the most flexible and most comprehensive interface for manipulating AVS/Express objects. The Network Editor and the V language interface are implemented using the C API to access Object Manager functionality. If you can perform an operation with the Network Editor or with V, you can perform it using the C API.

The C API contains C-specific routines implemented in the Object Manager (OM) library, as well as an assortment of other routines (for example, event-handling routines). This section defines some basic concepts used by the OM library and provides a high-level description of basic operations using the routines in this library.

For detailed descriptions of the C API routines, both OM and other, see the AVS/Express online help system.

Object Manager Data Structures

The Object Manager provides special data structures for three special types of data that it references:

Object IDs

AVS/Express assigns a unique ID to each object. In a C function, the type of an object ID is OMobj_id, which is defined in the header file <avs/om.h> as the following structure:

typedef struct _OMobj_id {
OMobj_id_type  obj_id; /* Pointer within a process */
OMproc_id  proc_id; /* Process ID */
} OMobj_id;

Each value in this structure is a 32-bit value.

Most OM library routines operate on the OMobj_id data structure. This structure is network transparent; that is, it can be passed from process to process, and machine to machine without change. It is generally passed around by value like an ordinary pointer.

Initializing Object IDs

When you initialize OMobj_id, you generally can ignore the fact that it is a structure. Note, however, that on some compilers, you cannot initialize a OMobj_id in its declaration. For example, the following initialization returns an error on some compilers:

OMobj_id id = OMnull_obj;

To be safe, initialize OMobj_id as follows:

OMobj_id id;
id = OMnull_obj;
Getting Object IDs

Using the appropriate C API routines, you can obtain the object IDs of AVS/Express objects. For example, the following V code defines the group object grp1 and two subobjects named a and b:

group grp1 {
int a;
int b;
};

A C function can obtain the object IDs of these objects as follows (assume that grp1's parent ID is parent_id):

OMobj_id parent_id, grp1_id, a_id, b_id;
...
grp1_id = OMfind_subobj(parent_id, OMstr_to_name("grp1"),OM_OBJ_RW);
a_id = OMfind_subobj(grp1_id, OMstr_to_name("a"), OM_OBJ_RW);
b_id = OMfind_subobj(grp1_id, OMstr_to_name("b"), OM_OBJ_RW);
Comparing Object IDs

Because an OMobj_id is a structure, you cannot compare two OMobj_ids using the `==' operator in C. To compare two object IDs for equality, you must use the C API routine OMequal_objs, as follows:

OMobj_id obj_id1, obj_id2;
obj_id1 = ...
obj_id2 = ...
if (OMequal_objs(obj_id1,obj_id2)) ...
Predefined Object-ID Variables

AVS/Express provides the following set of predefined variables of type OMobj_id, for use in your application:

Table I-1
Variable name
Description
OMinst_obj
The ID of the Applications object
OMnull_obj
A null object ID
OMroot_obj
The ID of the Root object
OMtempl_obj
The ID of the Templates object

The Object Manager defines these variables in the file <avs/om.h>; they are initialized at system start-up.

Identifying Null Object IDs

Some routines return the OMnull_obj value when they encounter an error condition. For information on how to test for this condition, see Object ID Return Values on page 9-20.

Object Names

Each object in an AVS/Express application has a name. One representation of this name is a name string: a character string that is unique among its sibling objects in the object hierarchy. The Object Manager does not store and manipulate this name string directly; instead, it allocates a unique integer value, stored in a C function as an OMobj_name type, that maps directly to the name string. This approach has two primary benefits:

Note that if your code calls a C API routine for which the object-name argument is optional, you can specify the constant OM_NULL_NAME to indicate no object name.

Getting an Integer-Value Object Name

Using the OMget_object_name routine, you can obtain the integer-value name of an AVS/Express object. For example:

/* Get the name of the object whose ID is func_id. */
OMobj_id func_id;
OMobj_name func_name;
int status;
...
status = OMget_obj_name (func_id, &func_name);
Converting Between Integer-Value Object Names and Name Strings

The C API also includes routines that you can use to convert between an integer-value object name and the corresponding name string.

For example:

OMobj_name object_name; // An integer.
char object_string[30];
...
object_name = OMstr_to_name("My string");
strcpy(object_string, OMname_to_str(object_name));

You should treat the name string returned from OMname_to_str as read-only storage. Do not free this string or reuse the memory to which it points.

Comparing Integer-Value Object Names

To compare two OMobj_name objects for equality, use the C equality operator (==).

Base Type IDs

Each object in an AVS/Express application has a base type. AVS/Express assigns a unique ID to each base type. In a C function, the type of each base type ID is OMtype_id. You need to use the OMtype_id type only if you are creating objects explicitly in a C function, which not a typical use of the Object Manager.

Object Manager Return Values

With few exceptions, OM routines return a status whose value is either an integer or an object ID (an OMobj_id value). If a routine does not return one of these values, the description of the routine in the online help system explicitly states the nature of its return value.

Integer Status Codes

Most API routines return an integer status code. It can have the following values:

Table I-2
Status code
Value
Meaning
OM_STAT_SUCCESS
1
The routine completed successfully.
OM_STAT_UNDEF
0
Soft error: the routine tried to get a value from an object with no defined value.
OM_STAT_ERROR
-1
Hard error: the routine failed for some other reason: for example, an invalid object ID or operation.

Some API routines generate error messages that contain diagnostic information when they fail with a status of OM_STAT_ERROR. These messages are printed to stderr. You can suppress them by "squashing" them for the duration of your code. (Typically, you squash errors only for one API routine at a time.)

To squash the error messages generated by an API routine, call the macro ERRsquash_start before the routine and ERRsquash_end after it. For example:

ERRsquash_start(); // begin suppressing errors
stat = OMget_int_val(obj_id, &val);
ERRsquash_end(); // end suppressing errors
Object ID Return Values

Several API routines return an object ID; for example, OMfind_subobj. If a routine of this type fails, it returns the value OMnull_obj for the object ID. You can test for this value with the routine OMis_null_obj. For example, the following code attempts to get the object ID of subobject x but prints a message if the attempt fails:

OMobj_id parent_id, x_id;
...
x_id = OMfind_subobj(parent_id, OMstr_to_name("x"), OM_OBJ_RW);
if (OMis_null_obj(x_id))
printf("Error searching for x\n");
Using the Mode Argument

Many OM routines take a mode argument as their last argument. The mode argument alters the behavior of the routine: it specifies a list of flags that are combined using the logical OR operation.

For a list of mode argument values for a specific OM routine, see its description in the online help system.

To use a routine's default behavior (that is, no optional modes), specify a mode value of 0. Note that a mnemonic constant that corresponds to this mode value may not exist.

Accessing the Object Hierarchy

One of the main operations performed in AVS/Express is traversing the AVS/Express object hierarchy to look for a specific object. Using a single set of traversal routines, you can find objects in a library, parameters in a module, modules in a macro, and so forth.

AVS/Express provides three IDs that allow you to access the base points of the object hierarchy. These IDs are stored in the following global variables:

Table I-3
ID
Value
OMroot_obj
The ID of the Root object
OMtempl_obj
The ID of the Templates object
OMinst_obj
The ID of the Applications object

Moving Up the Object Hierarchy

To move up the object hierarchy , use the OM routine OMret_obj_parent, as follows:

OMobj_id parent_id, obj_id;
obj_id = ...
parent_id = OMret_obj_parent(obj_id);

This routine returns the ID of the parent object of the obj_id argument. If this object has no defined parent, it returns OMnull_obj. An object may have no parent for the following reasons:

Moving Down the Object Hierarchy

There are two main ways to go down the object hierarchy:

Finding Subobjects by Name

If you know the name of the subobject that you want to access, you can use either OMfind_subobj or OMfind_str_subobj to find it. You use these routines as follows:

OMobj_id
OMfind_subobj(OMobj_id parent_id,
    OMobj_name sub_obj_name,
    int mode)

OMfind_str_subobj(OMobj_id parent_id,
 char *path_name,
 int mode)

The mode argument. If you cannot guarantee that changes will not be made to the object whose ID is returned, specify OM_OBJ_RW as the mode argument for both of these calls. If you can make this guarantee, specify OM_OBJ_RD instead. The value OM_OBJ_RD can prevent the creation of subobjects that are inherited from a base template. In this case, the returned object has the base template as a parent, not the object specified with parent_id. When in doubt, use the flag OM_OBJ_RW.

The returned subobject. OMfind_subobj returns the ID of an immediate subobject of the object specified by parent_id with the name specified by sub_obj_name; for example:

OMobj_id obj_id, parent_id;
parent_id = ...
obj_id = OMfind_subobj(parent_id,
OMstr_to_name("sub_name"), OM_OBJ_RW);

By comparison, OMfind_str_subobj can take a pathname to the subobject. The pathname can have multiple levels of path entries and can go up levels in the hierarchy using the <- notation. If the path_name argument has [n] specified, it can also go through array values. The following are examples of valid string values for the path_name argument:

obj_id = OMfind_str_subobj(parent_id, "A.B.C", OM_OBJ_RW);
obj_id = OMfind_str_subobj(parent_id,"B[3].C", OM_OBJ_RW);
obj_id = OMfind_str_subobj(parent_id,"<-.<-.C", OM_OBJ_RW);

If these OM routines do not find the specified subobject or another error occurs, they return OMnull_obj.

Finding through Libraries. The OMfind_subobj and OMfind_str_subobj calls can skip through more than one level in the AVS/Express object hierarchy if one of the immediate subobjects of parent_id is a library with the global attribute set. For example, consider the following V definition:

Root {
library+global Templates {
library+global UI {
group UIwindow {
...
};
};
};
};

Now consider the following call to OMfind_str_subobj (recall that the variable OMroot_obj contains the ID of the root object):

window_id = OMfind_str_subobj(OMroot_obj,
"UIwindow",
OM_OBJ_RW);

This call returns the ID of the UIwindow object. This is because both the Templates library and the UI library are global. To override the searching of global libraries, use the OR operator (|) to incorporate the OM_FIND_NO_LIBRARIES flag into the mode argument of OMfind_subobj or OMfind_str_subjobj. For example:

window_id = OMfind_str_subobj(OMroot_obj,
"UIwindow",
OM_OBJ_RW | OM_FIND_NO_LIBRARIES);

The Omfind _str_subobj routine now returns OMnull_obj instead of the ID of the UIwindow object.

Traversing Subobjects as a List

You can also traverse subobjects as a list.

Determining the number of subobjects. To determine how many subobjects a particular parent has, call OMget_num_subobjs, as follows:

int OMget_num_subobjs(OMobj_id parent_id,
     int *num_subobjs);

This routine takes the ID of a hierarchical object such as a library, macro, module, or group, and finds the number of immediate subobjects contained in the hierarchical object. It returns the following integer status values: 1 for success, 0 to indicate that the parent_id is a link object that does not have a current value, and -1 to indicate that the specified object does not define any subobjects.

Getting the subobject's ID. To get the ID of an individual subobject, call OMget_array_subobj, as follows:

int OMget_array_subobj(OMobj_id parent_id,
      int which,
      OMobj_id *child_id,
      int mode)

The subobjects of an object are specified in an ordered list. This routine returns in the child_id field the ID of the nth subobject in the list where n is specified by the which parameter. Set the mode flag just as you would if you were searching for a subobject by name (see Using a Mode Argument on page 9-80).

If you use Omget_array_subobj on an object that is a group, or on a module that has its reference mode set to `&' or is defined as an array of objects, it returns the IDs of the subobjects defined in the template object, not the IDs in any of the values. For example, suppose that you the following V definition of a group object:

group value {
int a;
};
group &ref {
int a;
} => value;

If you call OMget_array_subobj with parent_id set to the ref object and an index of 0, it returns the ID of the object ref.a, not value.a. To get the subobjects of the value, you must call OMget_obj_val to get the value and then use the ID it returns in the OMget_array_subobj routine.

Determining When Parameters Change

A module with an omethod (that is, a module whose method is implemented by a C function) has a sequence number parameter as the third argument of its callback function. To determine whether any parameter has been modified since the last time this method ran, specify this sequence number as the argument to the function; for example:

int
OMchanged(OMobj_id parameter_id, int seq_num);

OMchanged returns the following integer status values: 1 to indicate that the parameter has changed, 0 to indicate that it has not, and -1 to indicate an invalid parameter_id argument.

This is how the sequence-number process works.

1. Whenever OMpush_ctx called, the AVS/Express increments a global sequence number. An OMpush_ctx call proceeds the execution of each method, therefore each invocation of each method is guaranteed to have a unique sequence number.
2. When the value of a parameter changes, the parameter is marked with the current sequence number.
3. When the C function associated with an omethod is called, it is passed the sequence number corresponding to the last execution of the method as its third argument.

Therefore, any parameter with a sequence number greater than the sequence number passed in the third argument has changed. To return the sequence number for a selected object, you can use the OMget_obj_seq routine, as follows:

int
OMget_obj_seq(OMobj_id obj_id, OMobj_id templ_id, int mode)

The OMchanged routine simply calls this routine and compares the sequence number argument passed to it with the sequence number of the parameter. Using this lower level facility, you can keep track of the ordering of various events in the system.

Setting and Getting Data Values

The C API provides routines to set and get all data values that can be maintained using AVS/Express objects. There are several general rules for how these routines generally operate.

The first argument to the basic OM routines that set and get data values is the object ID (OMobj_id) of the primitive data object. For example, you specify the OM routine that gets an integer value from an object as follows:

int OMget_int_val(OMobj_id obj_id,
 int *returned_val)

If OMget_int_val returns an error, returned_val memory is not changed.

Before you can use this routine from a module, you must get the object ID of the parameter from the object ID of the parent, using the OMfind_subobj routine. For example, suppose that the V code for your module is as follows:

module my_module {
int p1;
omethod+notify+read update = "my_module_update";
};

The C source code for the my_module_update function is as follows:

int
my_module_update(OMobj_id obj_id,
OMevent_mask event_mask,
int seq_num)
{
OMobj_id p1_id;
int p1_val;
p1_id = OMfind_subobj(obj_id,
  OMstr_to_name("p1"),
  OM_OBJ_RD);
if (OMget_int_val(p1_id, &p1_val) != 1)
return(0);
/* operate on p1_val */
return(1);
}

For many data set and get routines, the OM library provides a convenience version that performs both the OMfind_subobj and OMget_int_val calls in one step. For OMget_int_val, the routine is called OMget_name_int_val. It is specified as follows:

int OMget_name_int_val(OMobj_id obj_id,
OMobj_name sub_name,
int *returned_val);

Using this convenience routine, you can write the following more succinct C code for my_module_update:

int
my_module_update(OMobj_id obj_id,
OMevent_mask event_mask,
int seq_num)
{
int p1_val;
if (OMget_name_int_val(obj_id,
   OMstr_to_name("p1"),
   &p1_val) != 1)
return(0);
/* operate on p1_val */
return(1);
}
Scalar Primitive Values

AVS/Express supports getting and setting the following types of scalar primitive values:

Table I-4
Data type
Get routine
Set routine
int, byte, short, char
OMget_int_val
OMset_int_val
float, double
OMget_real_val
OMset_real_val
string
OMret_str_val
OMset_str_val
ptr
OMget_ptr_val
OMset_ptr_val

Getting and Setting String Values

Getting a string value is different from getting an integer or a real value and thus deserves special mention. The routine OMret_str_val is declared as follows:

char *OMret_str_val(OMobj_id obj_id,
   char *buf,
   int buf_size)

If OMret_str_val finds the specified object, it returns a pointer to its string value. If obj_id is unset or has no defined string value, the routine returns NULL value. All valid strings returned are terminated with a NULL character.

String memory is managed in two ways:

This latter usage is typically implemented using code similar to the following:

OMobj_id obj_id;
char buf[128], *buf_ptr;

obj_id = ...
buf_ptr = OMret_str_val(obj_id,
 buf,
 sizeof(buf));
Getting and Setting Pointer Values

You use the pointer data type to pass information to user-defined data structures. It is more efficient and easier to use this data type than to describe the details of the structure as ints, floats, and so forth, but you sacrifice some flexibility. Pointer objects have the following restrictions:

Array Primitive Values

The C API supports getting and setting the following types of primitive arrays:

These array types are accessed with different OM routines.

Note: AVS/Express does not support pointer arrays.
Getting and Setting String Arrays

String arrays have two limitations:

To determine the size of a string array, use the routine OMget_array_size. To get a specific string value, use the routine OMret_str_array_val. To set a particular string value, use the routine OMset_str_array_val.

Getting and Setting Raw Data Arrays

You can either access raw data arrays in their entirety or process them as subarrays; that is, a piece at a time.

See Accessing Entire Arrays on page 9-30 for details.

Accessing Subarrays

When operating on arrays a piece at a time, you specify the range of each dimension that you want to set or get, then specify a piece of memory of the appropriate size for the operation. You then copy the data either into or out of this memory using the appropriate OMget_sub_xarray or OMget_sub_xarray routine. AVS/Express provides a different set of these routines for each data type it supports, as noted in the following table:

Table I-5
Data type
Get routine
Set routine
byte, char
OMget_sub_barray
OMset_sub_barray
short
OMget_sub_sarray
OMset_sub_sarray
integer
OMget_sub_iarray
OMset_sub_iarray
float
OMget_sub_farray
OMset_sub_farray
double
OMget_sub_rarray
OMset_sub_rarray

To use these OM routines, you must provide three pieces of information:

For example, here is the prototype for the OMset_sub_iarray routine:

int OMset_sub_iarray(OMobj_id id,
    int ndim,
    int *dims,
    int *min_rng,
    int *max_rng,
    int *array_ptr)

The prototypes for the other OMget_sub_xarray and OMset_sub_xarray routines are the same except for the data type of the array pointer argument. In the these prototypes:

(max_rng[0] - min_rng[0]) *
(max_rng[1] - min_rng[1]) *
...
(max_rng[ndim-1] - min_rng[ndim-1])
For OMset_sub_xarray routines, this memory is copied into the appropriate locations of the AVS/Express array object. For OMget_sub_xarray calls, AVS/Express returns the requested values in this memory.
Accessing Entire Arrays

How you access an entire array depends on whether you know the dimensions of the array before you begin to operate on it.

Array dimensions are known. When the dimensions of an array are defined in the AVS/Express object before you begin to operate on it (this is always true for for read-only array operations), start by having AVS/Express allocate and return a pointer to an array, then operate on that array. This technique works for all modes of array operations: read-only, write-only, and read-write. You use the following OM routines:

Table I-6
Routine
Common usage
OMret_array_ptr
Returns pointer, size, and type
OMret_typed_array_ptr
Returns pointer and size, forces array to specified type
OMget_array
General routine for advanced usage

Each of these routines takes an array mode that specifies how you will access the memory pointer returned to you. Four modes are available:

After using these routines, be sure to free the array by calling ARRfree. Calling this routine serves two purposes: it prevents memory leaks and it signals that you are finished with the array. If you specified the mode as OM_GET_ARRAY_WR or OM_GET_ARRAY_RW, a call to ARRfree also queues the events of any methods that requested notification on the array.

Array dimensions are not known. When the dimensions of an array are not defined in the AVS/Express object before you begin to operate on it, use the OMset_array routine. It is specified as follows:

int OMset_array(OMobj_id obj_id,
      int type,
      char *array_pointer,
      int size,
      int mode);

This routine provides flexibility in how AVS/Express allocates and frees the array. You first define the array using memory that you manage and then call OMset_array to tell the AVS/Express object about the new value for the array. At this time, events are queued for any methods that have requested notification on the array.

OMset_array takes an array mode that specifies how the memory passed in for this array is treated. Three modes are available:

One way to use this routine is to have AVS/Express point to an array in a FORTRAN common block or global definition in a C program. You can subsequently modify the array but you must then issue another call to OMset_array with the same pointer so that AVS/Express can notify methods that depend upon this array.
Returning Object Values to the Unset State

The basic primitive set commands do not provide a mechanism for deleting the value of an object and returning it to the unset state. To do this, you can call OMset_obj_val as follows:

OMset_obj_val(obj_id, OMnull_obj, 0);

This call also breaks any existing connections that the specified object may have.

Creating and Deleting Objects

This section describes how to create objects, either by copying them from templates or by parsing an object definition in a V buffer, and how to delete objects.

Creating Objects from Templates

AVS/Express uses the prototype model for object creation; that is, you use the copy operation to implement both templates (types of objects) and instances (active objects). Specifically, you copy an existing template object to create a new template objector an instance of any object.

Note: AVS/Express does not support copying instances.

The destination of the copy operation determines whether the object is instanced or not:

AVS/Express invokes only the methods of instanced objects.

There are four steps in the object-creation process:

1. Determine the pathname of the template object that you will copy.
2. Determine the parent of the object that you will create.
3. Create the object.
4. (Optional) Set the nosave attribute.
Determining the Template Pathname

Tthe pathname of a template object is usually of the following form:

library_name.object_name

For objects stored in a global library, you can omit the library-name component:

object_name

Do not use any global libraries in the pathname specification for an object: developers may change the names of these libraries. For example, the object named clamp is stored with the pathname MODS.Filters.clamp. Because the Filters object is global, you should specify the pathname of the clamp object only as MODS.clamp. This gives you the flexibility of moving the clamp object from the intermediate Filters library without changing your code.

Templates in global libraries are defined for the common base types such as group and int so that you can create these objects using a pathname equal to the base type name.

Determining the Object's Parent

When determining the parent for the object you are going to create, note that if you are creating an instanced object, the parent should be somewhere in the Applications hierarchy (perhaps even the Applications object itself).

If you are creating your object from a module's update method, you must determine whether you want this object to be deleted automatically when your module is deleted. If so, you might create a group within your module to serve as a destination for dynamically created objects.

You must also determine whether you want the Network Editor to be able to access the object. This may affect your choice of a destination for the new object. Note that you can set the NEvisible property to prevent your object from being edited in the Network Editor even if you create it as a subobject of an object that the Network Editor displays.

Creating the Process

The simplest way to create a new object is to call the OMcreate_obj_from_path routine, specified as follows:

OMobj_id OMcreate_obj_from_path(char *pathname,
  char *new_name,
 OMobj_id parent);

The parameters for this routine are the pathname of the template object (see Determining the Template Pathname on page 9-33), a character string that contains the name for the new object, and the object ID of the parent object. If an object already exists with the specified new name, OMcreate_obj_from_path chooses a unique name of the form new_name#1 (or new_name#2 and so forth, as appropriate). When it finishes, OMcreate_obj_from_path returns the ID of the object that it creates. If an error occurs, for example in finding the specified template object, it returns OMnull_obj.

Setting the nosave Attribute

If you created your object in a hierarchy that will be saved, you must determine whether or not you want this dynamically created object to be saved with the rest of the hierarchy. If not, set the nosave attribute on the object (see General Properties and Attributes on page 14-3).

Creating Objects Using V Code

If you are creating an object such as an array that has complex dimensions or properties, a simpler approach may be to generate a buffer that contains a V definition of the object and then use the V parser to create the object. To do this, you use the OMparse_buffer command.

The following example creates two int objects named bar and foo that are placed into the parent object specified by parent_id:

char *buf = "int bar; int foo[bar*2];";
OMobj_id parent_id;
parent_id = ...
status = OMparse_buffer(parent_id, buf, 0);
Deleting Objects

To delete objects, use the OMuser_destroy_obj routine. This routine first breaks all connections to objects that are subobjects of the object being deleted, then destroys the specified object. The destroy callbacks for any objects that are deleted in this operation are called during this routine.

Manipulating Connections

This section describes making and breaking connections, following connections, and following pointers, and references to determine object values.

Making and Breaking Connections

The C API contains a number of OM routines that you can use to make and break connections.

For objects that can have only a single connection (scalar primitive objects, scalar group references, and links), OMset_obj_ref is the most useful choice. This routine replaces any existing connection with the specified new object connection. You can also use it with array objects when connecting one array object to another array object. It is declared as follows:

int OMset_obj_ref(OMobj_id from_id,
 OMobj_id to_id,
 int mode);

Calling OMset_obj_ref is equivalent to the following V statement:

from_object => to_object;

The mode argument for this routine is typically 0. However, you can set mode to OM_OBJ_REF_RDONLY to make connection that allows only get operations through the connection. In this mode, set operations on the from_id object break the connection.

To break a connection with OMset_obj_ref, specify the to_id argument as OMnull_obj.

For objects that can have arrays of connections, an appropriate choice is OMadd_obj_ref, declared as follows:

int OMadd_obj_ref(OMobj_id from_id,
 OMobj_id to_id,
 int mode);

This routine adds a new connection from the array from_id to the scalar to_id. While you can use OMadd_obj_ref with a scalar object as the from_id argument, it returns an error if the object has an existing connection.

The mode argument for OMadd_obj_ref is typically 0. However, you can set mode to OM_OBJ_REF_QUERY to determine whether a connection can be made without actually making the connection. In this case, the routine returns 1 if the connection is can be made and 0 if it cannot.

To break a single, specific connection in an array of connections, call OMdel_obj_ref as follows:

int OMdel_obj_ref(OMobj_id from_id,
 OMobj_id to_id,
 int mode)

This routine deletes the connection from from_id to to_id. The mode argument for this routine is always 0.

Following Connections

If you want to traverse connections as they are displayed in the Network Editor, you use two routines: OMget_num_refs and OMget_array_ref.

OMget_num_refs determines the number of connections from a specified object.

The following table shows, for various V statements, how many connections OMget_num_refs would report for the "from" object:

Table I-7
V statement
# connections
int from;
0
int to;
int from => to;
1
int from[3];
0
int to[3];
int from[3] => to;
1
int to_0, to_1, to_3;
int from[3] => {to_0, to_1, to_3}
3
int to;
int from[3] => to; // same value as {to, to, to}
1

OMget_array_ref finds the destination of a specified connection. You provide an integer index argument that is less than the value supplied by the OMget_num_refs call.

Note that if you are dealing with arrays of references to groups and you use the OMget_array_ref call to access the values of each individual group, your code will not work properly if the group either is defined as a single connection to an array of groups or is not defined as a local array. In this case, use the OMget_array_val routine.

Determining Object Values

Each AVS/Express object has an object value. Object value is defined differently for primitive objects (int, float, and so forth) than for group objects. You can determine an object value either in V, using the $obj_val command, or from the C API routine OMget_obj_val.

Table I-8
V statement
Object value of obj
int obj;
obj
int val = 3;
int obj => val;
val
int val;
int obj => val;
val
int val = 3;
int obj => val * 2;
obj
int val[3] = {0,1,2};
int obj[3] => val;
val
int val;
int obj[3] => {val,val+1,val+2}
obj
int val;
int obj2 => val;
int obj => obj2;
val

Table I-9
Table I-10
V statement
Object value of obj
group obj;
obj
group val;
group &obj => val;
val
group val;
link lk => val;
group &obj => lk;
val
group val[3];
group &obj[3] => val;
val
group &obj;
<value not set>
group v1, v2, v3;
group &obj[3] => {v1,v2,v3};
{v1,v2,v3}
group val:
group &obj2 => val;
group &obj => obj2;
val

The C API routine OMget_obj_val returns a 0 status if the value is not set (as is the case with a group reference that is not connected to another object).
Managing Arrays of Groups, Modules, and Macros

AVS/Express is limited to operating on one-dimensional arrays of groups, modules, and macros. Because you manage arrays of these three object types identically, this section describes management techniques only for arrays of groups. To manage arrays of modules or macros, simply follow the same instructions but substitute module or macro for group.

Accessing Arrays of Groups

There are three ways to specify an array of groups:

Table I-11
V definition
Description of group
group foo[3];
A locally defined array of groups
group bar[3];
group &foo[3] => bar;
A reference to an array of groups
group bar1, bar2, bar3;
group &foo[] => {bar1,bar2,bar3}
An array of references to groups

You use the same commands to access an array of groups regardless of how it was specified.

To determine the number of groups defined in an array, use the OMget_array_size routine. This routine returns 0 if the array dimensions are not set or the group's value is not set.

To get information about a specific array element, use OMget_array_val. This routine takes as arguments the ID of the array of groups and an index into the array, and typically returns the ID of the specified element. In some cases, however, it returns the ID of a name-link object rather than the ID of a group itself. When this happens, you can use the OMget_obj_val routine on the returned ID to skip through the name-links. Calling OMget_obj_val allows you to handle properly the error case that occurs when the specified element of an array is not set. For example, this error occurs with the following V definition:

group &v1;
group &foo[] => {v1};

In this case, the OMget_array_size function returns a size of 1 and OMget_array_val returns the ID of the v1 object. However, because the value of v1 is not set, you should not process it as a valid object. OMget_obj_val detects this case and returns a 0 status.

Creating Arrays of Groups

You can create arrays of groups from the C API in two different ways:

Explicit Arrays

The following example V code defines an explicit array of groups:

int nfoos;
group foo[nfoos] {
int sub1= 10;
int sub2;
};

Resetting the value of nfoos automatically resizes the foo array.

An alternate way to specify the dimensions for an explicit array of groups is to use the following V syntax:

group foo[] {
int sub1;
};

When you use this syntax, you should set the size of the foo array with the OMset_array_size routine.

Defining an explicit array of groups has two limitations:

References to Arrays

By defining your array of groups as a reference to an array of groups, you can create an array of groups over which you have more control. However, this technique is more complicated, because you must handle the creation and destruction of the individual array objects and manually insert them into the right location of the array of group references. However, because you perform the create/destroy and insert/remove processes separately, you can arrange the objects in the array as you choose.

Creating Objects for Arrays of Groups

For general instructions on creating array objects, see Creating and Deleting Objects on page 9-32. This section contains some notes specific to array objects (groups, in this case).

As noted previously, you must have a destination object that stores your newly created groups. You may also want to create an empty group object in your module or elsewhere in your application to contain these objects and serve as a parent for managing them. Finally, you must decide whether to set the nosave attribute on objects that you create, to prevent them from being saved and restored with your application.

When creating an array object, you must ensure that it matches the template declaration for your array of groups. If not, an error will occur when you try to add the object to the array of groups. For more infomatiom see Viewing and Setting an Object's Reference Mode on page 5-51

You can define an array of groups to have either explicit or unspecified dimensions. Note that if the dimensions of your group are unspecified, inserting and removing objects is much easier. When an array's dimensions are unspecified, the array size is determined automatically as you add or delete entries. If you need the size of the array as a separate integer object, you can use the array_size V function, as follows:

group &array[];
int size => array_size(array);

If your groups are defined using this syntax, you can perform the following operations on your array of groups:

Note that while you can add the same object to an array more than once, you can remove only its first occurrence in the array.

If you specify explicit, rather than unspecified, dimensions for your array, you can use the OMset_array_val routine to replace a specific entry in it.

Manipulating Properties and Attributes

Properties and attributes attach additional information to an object's structure.

For descriptions of specific properties and attributes, see Properties and Attributes on page 14-3.

Getting and Setting Properties

Properties have simple primitive values such as integers, strings, floating-point numbers and arrays of these values. They cannot be connected to other objects and cannot have hierarchical object values. The following table lists the C API routines with which you set and get properties for various data types:

Table I-12
Data type
Set property routine
Get property routine
integer
OMset_obj_iprop
OMget_obj_iprop
float/double
OMset_obj_rprop
OMget_obj_rprop
string
OMset_obj_sprop
OMget_obj_sprop

In these routines, properties are identified using the OMobj_name structure (see Object Names on page 9-18). Some standard system properties are predefined in the include file <avs/om_att.h>. You can either use these global definitions or call the OMstr_to_name routine to convert a string to a property identifier.

Setting the Network Editor Port Properties

The Network Editor define the NEportLevels property as an array. This array has two integer values that specify the number of levels to export the input and export ports, respectively. To set these values to the same value, you can use the OMset_obj_iprop routine. However, if you want to set the input and output levels to different values, you must use the OMset_iarray_prop routine, defined as follows:

OMset_iarray_prop(OMobj_id obj_id,
 OMobj_name property_name,
 int nvals,
 int *vals);

To set the NEportLevels property, you call this routine as follows:

int vals[2];
vals[0] = 0;
vals[1] = 2;
OMset_iarray_prop(obj_id,
 OMstr_to_name("NEportLevels"),
 2,
 vals);
Getting and Setting Attributes

Attributes are special Boolean values associated with an object. AVS/Express defines a specific set of attributes, for example read, write, and notify; you cannot define new attributes. Each attribute is identified in the include file <avs/om_att.h> by a global variable having the following form:

OM_att_attribute_name

You can query the state of an attribute by calling the routine OMget_obj_att and specifying the associated variable. For example, to inquire whether the read attribute is set on an object, call OMget_obj_att as follows:

OMobj_id obj_id;
int state;
obj_id = ...
if (OMget_obj_att(obj_id,
 OM_att_read,
 &state) == 1 && state == 1) {
// read attribute is set
}

To set or clear a particular attribute on an object, call the OMset_obj_att routine, referencing the relevant variable.

Saving and Restoring Object Descriptions

AVS/Express supports state save and restore operations in two different formats:

You can use C API routines to perform save and restore operations in either format.

Saving to Text V Files

When you save object descriptions in a text V file, you specify the objects to be saved by supplying the object ID of the parent of the subtree of the relevant AVS/Express object hierarchy. AVS/Express then saves a V file that contains derived object definitions for the saved objects. (The Object->Save Objects command in the Network Editor uses this operation.) When you restore (load) the file, AVS/Express simply recreates the object hierarchy. Note that you can restore a V file only if any templates referenced by it exist in the running version of AVS/Express.

Suppose, for example, that you create an object named my_button from the template object UIbutton, then save it to a V file. The V file contains this definition for my_button:

UIbutton   my_button {
width = 250;
height = 100;
};

To restore this definition successfully, the system must have a definition of the UIbutton object.

You use the C API routine OMwrite_obj, defined as follows, to save objects to a V file:

int OMwrite_obj(OMobj_id obj_id,
      char *filename,
      int mode)

In this definition, obj_id is the object ID of the parent of the subtree of the object hierarchy to be saved, and filename is the name under which to save it. If a file having the specified filename already exists, AVS/Express replaces it unless you set the mode argument to OM_WRITE_APPEND.

You can also use the mode argument to specify bit flags that determine what parameter values in the object descriptions are saved. Parameter values are tagged with a state flag that indicates one of three possible states for the value:

Table I-13
State name
Description
Program state
Identifies values that are intrinsically part of the definition of the application, including connections and values that are not modified during the course of execution of the application. The positions and sizes of widgets may fall into this category. These values are always saved using the OMwrite_obj routine.
User state
Identifies values that are modified by the user of an application. These values are typically the ones directly controlled by a user interface widget; for example, the filename parameter connected to a file selection box.
Transient state
Identifies values that are modified by a module that can reproduce these values if the inputs to the module are saved. For example, image data for a read image module is typically marked as transient state because it can be reproduced if the filename parameter is saved.

Set the mode argument as follows to specify particular combinations of parameter types to save:

For information about changing the state mode when writing a module, see Event Notification of Methods on page 8-11.

Restoring Text V Files

There are several ways to restore text V files.

Reading V Statements from a File

To read V statements from a files, call the OMread_desc routine, defined as follows:

int OMread_desc(OMobj_id obj,
      char *filename,
      int mode)

OMread_desc reads the specified V file so that objects defined in the file become subobjects of the object specified by the obj parameter. Specify the mode argument as 0. This routine returns 1 to indicate succes, 0 to indicate that errors were encountered while reading the file, or -1 to indicate that an invalid object ID was specified.

Parsing a Buffer of V Commands

To parse a buffer of V commands, call the OMparse_buffer routine, defined as follows:

int OMparse_buffer(OMobj_id obj, char *buffer, int mode)

The buffer argument specifies a null terminated buffer of V statements. These statements are parsed in the same way that the file in the OMread_desc routine is parsed. Any objects defined in the buffer becomes subobjects of the specified object.

Parsing an Object's Value

To set an object's value from a V value string call the OMparse_obj_val routine, defined as follows:

int OMparse_obj_val(OMobj_id, char *value_string)

The value of the specified object is set to the specified value string. For example, consider the following call to this routine:

status = OMparse_obj_val(my_obj, "10 * a");

This call is equivalent to the following V statement:

my_obj = 10 * a;

Note that this mimics the = operator in V. To mimic the => operator instead, call the routine OMparse_obj_ref. For example, consider the following call to this routine:

status = OMparse_obj_ref(my_obj, "10 * a");

This call is equivalent to the following V statement:

my_obj => 10 * a;
Parsing V Statements from a Stream

To parse V commands from a stream, call the OMparse_stream routine, defined as follows:

int OMparse_stream(OMobj_id obj_id, FILE *fp, int mode)

This routine operates in the same way as the previous two routines, except that it treats the specified stream as an interactive stream of V commands specified by the fp parameter. This routine implements the V command processor (VCP).

Saving to Binary V Files

AVS/Express supports save operations to two types of binary V files.

AVS/Express flibrary objects use this type of binary V file to create a binary description of the information stored in the V file specified by the libfile property.
The Project->Save Compiled Project command in the Network Editor uses this operation.

You use the OMbsave_objs routine, defined as follows, to create both types of binary V files:

int OMbsave_objs(int nobjs,
OMobj_id *obj_id_list,
int nex_objs,
OMobj_id *ex_templ_list,
char *filename, int mode)

The nobjs and obj_id_list parameters specify a list of objects to be saved in the binary V file. When you restore the binary V file, these objects are restored as peers in the AVS/Express object hierarchy; therefore, they typically should be peers in the AVS/Express object hierarchy at the time of the save.

Note: Do not specify an object that is the parent of another object in the list.

The filename parameter specifies the name under which to save the binary V file.

The mode argument specifies which type of binary V file to create.

Restoring Binary V Files

Use the OMopen_file routine, defined as follows, to restore (read in) either a binary V file or a text V file:

int OMopen_file(OMobj_id obj_id,
      char *filename,
      int mode)

The objects in the V file are loaded as subobjects to the object specified by obj_id.

9.4 C++ API Usage Notes

C++ programmers are free to use the routines in the C API (the OM library) to manipulate AVS/Express objects, but they can also use the routines in the C++ API. This API, implemented in the OMX library, takes advantage of C++ features to provide a more intuitive interface. This section defines some basic concepts used by this library and provides a high-level description of basic operations using the routines in this library.

For detailed descriptions of the C++ API routines, see the AVS/Express online help system.

Using the OMX interface, you access objects directly, using C++ names to refer to AVS/Express objects of the same name. For example, consider the following C++ statement:

read_image.filename = "my_image.x";

This statement assigns the string value "my_image.x" to the read_image module's filename parameter. The C++ structure for the read_image object parallels the object hierarchy in AVS/Express; member variables in C++ correspond to subobjects in AVS/Express. For AVS/Express objects that manage access to dat (for example, the filename parameter in this exampl), the operator-overloading feature of C++ provides direct access to data.

C++ programmers can use OMX in two ways:

During both imports and exports of code, the C++ programmer has a C++ class that is associated with the AVS/Express object. In both cases, the member variables of the C++ object correspond to the subobjects of the AVS/Express object. Once you have created the class, the interface is the same whether you are using this class to import or export code.

Getting Pointers to C++ Objects

When writing modules, you receive a pointer to your C++ object as the implied this argument to your method. For example, consider the following V code:

module foo {
int+write parameter_a;
cxxmethod update;
};

The C++ code to set parameter_a to 1 is as follows:

foo::update(OMevent_mask event_mask,
  int seq_num)
{
this->parameter_a = 1;
return(1);
}

In C++, you can omit the this-> string to write this code more more succinctly, as follows:

foo::update(OMevent_mask event_mask,
  int seq_num)
{
parameter_a = 1;
return(1);
}

In order to export code from the AVS/Express environment, you must get a pointer to the C++ object that corresponds to a particular AVS/Express object. To do this:

This extra level of complexity is necessary because each object may be derived from several superclasses, each of which may have different pointers.

The following code fragment creates a new AVS/Express object of the Field class:

Field *my_field = new Field;
my_field->nnodes = 3;

When you use a C++ object to create an AVS/Express object and then use the C++ delete operator on that object, you destroy the AVS/Express object.

The constructor for the object takes an optional argument that determines which AVS/Express object is used as its parent. The default parent is the Root.Applications object, which is an acceptable place to place new objects; however, you may want to create a container object for the objects. A container object allows better debugging using the Network Editor. To do this, create an application object, then use this object as the first argument to the constructor:

OMXappl *appl = new OMXappl;
Field *my_field = new Field(*appl);

If the Network Editor contains an existing instance of a Field object with the pathname SingleWindowApp.Field#1, get a pointer to a C++ object that controls this object, as follows:

// Get the object id
OMobj_id field_id = OMfind_str_subobj(OMinst_obj,
   "SingleWindowApp.Field#1",OM_OBJ_RW);

if (!OMis_null_obj(field_id)) { // If we can find the object...
Field *my_field =
   ((OMXgroup *)OMret_omx_ptr(field_id,0))->ret_omx_ptr("Field");
my_field->nnodes = 3;
}

When using OMret_omx_ptr, do not use the C++ delete operator on this object. There is a single C++ object that is shared by all users of the corresponding AVS/Express object, and it is deleted automatically when the object itself is destroyed. To destroy the object, use the routine OMuser_destroy_obj. To access the pointer to the object in this manner, you should be in the same process as the object being manipulated. In case of error, a 0 pointer is returned.

Accessing Subobject Values

You can access the values for AVS/Express subobjects through member variables of the C++ class. Although there is generally a one-to-one correspondence between the member variable hierarchy of the C++ class and the AVS/Express object hierarchy, there are several exceptions:

Each AVS/Express object has a corresponding OMX class that implements the C++ programmer's interface to control that object. The following table shows the mapping between the AVS/Express types and the OMX types:

Table I-1
OMX type
AVS/Express type
OMXint
int, byte, char, short
OMXreal
float, double
OMXprim
prim
OMXprim_array
prim[], char[], byte[], short[], int[], float[], double[]
OMXgroup
group, module
OMXappl
application
User-defined group
Generated C++ subclass of OMXgroup
OMXgroup_array
group[]
User-defined group array
Generated C++ subclass of OMXgroup_array

The following sections describe how to use each of these different types of OMX object classes.

C++ Interface to Scalar Data

Scalar data objects are implemented by a C++ class that is customized for their particular types. For example, an integer is implemented with a C++ class called OMXint. Each of these C++ classes overloads the C++ operators so that these objects can be treated as though they are stored as the particular type. If you try to assign a a value to an OMXint object, the value is cast into an integer and a method is called to perform the assignment. You can use the C++ member objects in most operations where ordinary integers are used: assignments, expressions, comparisons, and so forth. For example, all of these operations are valid using the previous foo.parameter_a example:

if (foo.parameter_a == 1)
foo.parameter_a = 2 * foo.parameter_a;

There are a few cases in which you cannot use these objects like ordinary values:

int *foo_ptr = &foo.parameter_a;
foo.parameter_a = (int) foo.parameter_b;
For strings, you can use an assignment statement like this:
foo.parameter_a = (char *) foo.parameter_b;
if (foo.parameter_a.valid_obj())
cout << "object parameter a is valid" << endl;
else
cout << "object parameter a is not valid" << endl;
String values operate much like ints, floats, and so forth; you can use a string object to produce a pointer to a null-erminated character string. Do not free the character string; the space is managed by AVS/Express. If an AVS/Express string object has no defined value, a NULL character string is returned.

You can also assign a character string to an AVS/Express string object. AVS/Express makes a copy of the string passed during the assignment. Assigning a NULL pointer to a string object is equivalent to setting the string to an unset state.

C++ Interface to Array Data

The C++ interface does not handle arrays of ints and floats as transparently as it handles scalar ints and floats: you cannot directly access them using the [] operator. There are two basic ways to access arrays:

Accessing Array Data Using ret_array_ptr

If the dimensions of your array are defined before you need to access or modify any of its values, use the ret_array_ptr method to get a pointer to the array. For example:

void *ret_array_ptr(int mode = OM_GET_ARRAY_RW,
   int *size = NULL,
   int *type = NULL);

You use the mode argument to specify how you will use the array.

The size and type arguments (described later in this section) default to NULL, thus you can omit them for many uses of this routine.

Suppose that your object is defined as follows in V code:

group foo {
int bar[3];
};

You can to access it as follows:

foo *foo_ref = new foo;
int *my_array_ptr =
(int *)foo_ref->bar.ret_array_ptr(OM_GET_ARRAY_WR);
my_array_ptr[0] = 0;
my_array_ptr[1] = 1;
my_array_ptr[2] = 2;
ARRfree((char *) my_array_ptr);

When you are done with the pointer returned by the ret_array_ptr routine, free it with the ARRfree routine. Failing to free this pointer results in a memory leak and prevents AVS/Express from propagating events that are registered on the array object.

If the array dimensions are not defined at the time that the call is made, the ret_array_ptr routine returns a NULL pointer . It is especially important to note that if your object is defined as follows, you must set nvalues before you try to get the pointer to the array:

group foo {
int nvalues;
int array[nvalues];
};

This is the correct order:

nvalues = 3;
int *my_array_ptr =
(int *)foo_ref->array.ret_array_ptr(OM_GET_ARRAY_WR);

By comparison, the following produces an error and returns a NULL pointer:

int *my_array_ptr =
(int *)foo_ref->array.ret_array_ptr(OM_GET_ARRAY_WR);
nvalues = 3;

It is important to match the type and size of an array properly. The ret_array_ptr method optionally returns the array in the most efficient data type that it can. It also returns an array that contains the number of values specified in the current definition of the AVS/Express object. If the AVS/Express definition leaves the type and/or size ambiguous, as in the following V definition, you can have the ret_array_ptr method return the type and size of the array with the defaulted second arguments:

prim array[]; // "prim" means type is not set
// [] means dimensions are not set

The ret_array_ptr method returns a size that is the total size of the array (that is, the product of all dimensions). It returns a type that is one of the following constants: OM_TYPE_CHAR, OM_TYPE_BYTE, OM_TYPE_SHORT, OM_TYPE_INT, OM_TYPE_FLOAT, or OM_TYPE_DOUBLE.

For example:

//
// foo points to the group containing array...
//
int size, type;
void *my_ptr =
foo->array.ret_array_ptr(OM_GET_ARRAY_WR,&size,&type);
switch (type) {
case OM_TYPE_FLOAT:
float *my_float_ptr = (float *)my_ptr;
for (int i = 0; i < size; i++) my_float_ptr[i] = 10.0;
break;
case OM_TYPE_DOUBLE:
double *my_double_ptr = (double *)my_ptr;
for (int i = 0; i < size; i++) my_double_ptr[i] = 10.0;
break;
}
ARRfree(my_ptr);

The ret_array_ptr method always returns an array with a data type that matches the definition of the type in AVS/Express. If you need to force the array to return a type that is different from the type specified in AVS/Express, use the method ret_typed_array_ptr; for example:

void *ret_typed_array_ptr(int mode = OM_GET_ARRAY_RW,
   int type = OM_TYPE_INT,
   int *size = NULL);

The mode argument specifies the same mode as ret_array_ptr. Unless the type argument is set to OM_TYPE_UNSET, the array is forced to the specified type; for example, setting it to OM_TYPE_FLOAT always returns a pointer to an array of floats. The conversion rules are the same as those for a C cast operation. The size argument returns the number of values in the array if NULL is not specified as this argument.

Accessing Array Data Using set_array

If the dimensions of the AVS/Express array object are not defined until after you start operating on the array, use the set_array method to get a pointer to the array. With set_array, you build the array in memory that you allocate and give the array to AVS/Express only when you are done. For example:

int set_array(int type, char *array,
    int len,
    int mode = OM_SET_ARRAY_COPY);

The type argument must be OM_TYPE_CHAR, OM_TYPE_BYTE, or similar. The array argument contains a pointer to the array. The len argument is the number of values in the array. (For an array of ints, this is the number of ints, not the size in bytes of the array.) The mode argument provides several ways to manage memory, in order to prevent making unnecessary copies of data. It can be one of the following:

C++ Interface to Group Arrays

In OMX, group arrays are implemented by overriding the [] operator so that you can effectively treat the array of groups naturally in C++. For example, consider this V definition:

group foo {
group bar[3] {
int a, b;
};
};

You can reference the subobjects of the group array bar as follows:

for (int i = 0; i < 3; i++) {
foo->bar[i].a = 10;
foo->bar[i].b = 20;
}

Note that you can reference the values of an array of groups only if its dimensions are already defined. If, for example, consider this V definition:

group foo {
int ngroups;
group array[ngroups] {
int a;
};
};

You must set the ngroups value before you can reference this array:

foo->ngroups = 1;
foo->array[0].a = 10;

To inquire the size of an array of groups (or any array), use the ret_array_size method. For example:

int size = foo->array.ret_array_size();
for (int i = 0; i < size; i++) {
foo->array[i].a = 10;
};

Note that this method takes no arguments.

Additional C++ Methods

Many C API routines defined in the OM library have OMX method equivalents. Typically, an OMX method takes the same argument list as the corresponding OM routines, with one exception: it omits the first OMobj_id argument, and the object whose method is being invoked becomes the implicit first argument. In addition, some routines also have default values specified for the arguments at the end of the parameter list, making these routines more convenient to use.

For example, you call the OMpush_ctx routine using the OM library as follows:

OMobj_id my_id = ...
OMpush_ctx(my_id, 0, 0, 0);

You can perform the same call using the OMX library as follows:

foo *my_obj = ...
my_obj->push_ctx(0,0,0); // first argument is omitted

The 0,0,0 values also happen to be the defaulted values for the push_ctx method, so you can also call this routine more succinctly as follows:

my_obj->push_ctx();
Using OM Routines with an OMX Object as an Argument

All OMX object types implement the cast operator to produce an OMobj_id. This makes it easy to use any OM routines using an OMX object as an argument directly. For example, you can call the OMret_obj_name routine as follows:

foo *my_foo_ptr = ... // get a pointer to a foo object
// casts my_foo_ptr to an OMobj_id
char *my_name = OMret_obj_name((OMobj_id)*foo_my_ptr);
Note: Some C++ compilers cannot easily determine how to cast an OMX object to an OMobj_ID value automatically, so you must be sure to include the explicit cast in the statement.
Determining When Parameters Change

The changed method and the second argument to a user-written method associated with a module work in concert to indicate whether an object has changed since the last time the method was called. The changed method returns 1 to indicate that the parameter has changed or 0 to indicate that it has not. For example, consider the following V definition of a module:

module foo {
int+read+notify bar;
cxxmethod update;
};

In the C++ method you write to implement the module's update method, you can use the following code to determine whether the bar parameter has changed:

foo::update(OMevent_mask event_mask, int seq_num)
{
if (bar.changed(seq_num)) {
cout << "bar has changed" << endl;
}
return(1);
}
Manipulating Connections

The OMX routines that deal with making and breaking connections parallel the OM routines with the same names:

int add_obj_ref(OMobj_id conn_id,
      int mode = 0);
int del_obj_ref(OMobj_id conn_id,
      int mode = 0);
int set_obj_ref(OMobj_id conn_id,
      int mode = 0);

Each of these routines takes an OMobj_id value as the first argument. Because the OMX objects implement the cast operator to produce an OMobj_id, you can directly specify the name of an OMX object as the first argument. The second argument is a mode that defaults to the value you want to use for normal connections. Suppose that you have a C++ class generated from the following V code:

group foo {
int a, b;
};

You use set_obj_ref to replace any existing connections from a source object with either a new connection to a target object or a null connection. (You create a null connection to break the existing connections.) You use add_obj_ref to add a new connection. To delete a specific connection, you use del_obj_ref.

Creating a Connection

You can simply connect object a to object b as follows:

a.set_obj_ref((OMobj_id)b);

The set_obj_ref method replaces any existing connections with the specified new connection. You can use this same method to break a connection, as follows:

a.set_obj_ref(OMnull_obj);
Adding a Connection

The add_obj_ref method adds a new connection to the source object. You can use it to connect a source array object to a destination scalar object (or, through multiple calls, a list of destination scalar objects). If the source object is a scalar, this routine returns an error if the source object is already connected to another object.

Suppose that you have an object whose V definition is as follows:

group foo {
int a[], b;
};

You can use add_obj_ref as follows to connect object a to object b.

a.add_obj_ref((OMobj_id) b);

This new connection is added to the list of object a's connections (it does not replace those connections). The new connection creates the following value expression for object a:

a => { b };
Deleting a Specific Connection

The del_obj_ref method deletes a connection from the source object to a specific destination object. For example, you can delete a connection from object a to object b as follows:

a.del_obj_ref((OMobj_id) b);
Mapping AVS/Express Object Names to C++ Class Names

When AVS/Express generates a C++ class for an object, it must assign it a name that is unique across the system. However, AVS/Express object names are guaranteed to be unique only for the specific parent that contains them. To work around this potential problem, AVS/Express generally creates a name by prepending the name of the containing library onto the name of the AVS/Express object. The exception is that it does not prepend the library name if the library is a global library.

For example, the FLD library is global and its names can be seen at peer level, so you do not have to type the following to create a field in V:

FLD.Field new_field;

Instead, you can simply type:

Field new_field;

Because the FLD library is global, the C++ class name for the Field object is simply Field. If the FLD library was not global, the C++ class name would be FLD_Field (note that an underline character (_) replaces the period as the separator between the library name and the object name).

You can change the C++ name for any AVS/Express object using the cxx_name property. This works for AVS/Express objects that become member variables, C++ class identifiers, and nonglobal library names that are prepended to the class name for an object.

Managing C++ Generated Files

When AVS/Express generates a C++ class description for an object, it places header code into the file specified by the current value of the out_hdr_file property and source code into the file specified by the current value of the out_src_file property. You can set these properties either directly on the AVS/Express object or on the library that contains the object. If you do not set these properties, AVS/Express uses the defaults of process_name.cxx for source and process_name.h for header information, where process_name indicates the process in which the object is defined ( for example, express). These files are placed in your project directory.

Note: Do not rely on these defaults except for very temporary projects: the definitions change from file to file as the process property on the object changes. This makes writing code that consistently includes these files problematic. Therefore, when defining a project of significant scope, you should define values for these properties.

If you specify the out_src_file property without a suffix, AVS/Express generates a file whose name has a the suffix .c if none of the file's objects have C++ references, otherwise it generates a file whose name has the suffix .cxx.

When AVS/Express generates a file specified by out_hdr_file, it always includes ifdef__cplusplus constructs in the file so that they can be included in C code as well as in C++ code.

Managing Dependencies on Other Code

In a number of cases, a C++ class generated by AVS/Express may depend upon other code in the system, either user-written code or other C++ class definitions generated by AVS/Express. These dependencies can be generated from the following:

Unless you provide additional header information, the generated C++ code will not compile properly. To provide this additional header information, you can indicate the dependencies using the cxx_hdr_files property. (You can set this property either on the module or on the library that contains the module.) To set it, specify a string value that contains a list of file names; for example:

module subroutine_call<cxx_hdr_files="user1.h user2.h"> {
...
};

These filenames are defined relative to either of the following

By default, this list includes the top-level project directory and the include subdirectory of each project in the XP_PATH variable. You can extend this list with the hdr_dirs property.

When a dependency is generated because an AVS/Express object references objects in a library with a different setting for out_hdr_file, it can be difficult to determine the particular dependency. This type of dependency is generated by the definition of the AVS/Express objects rather than by user-written code.

The following example defines two libraries, each with a single object:

library A<out_hdr_file="A.h"> {
group Aobject {
};
};
library B<out_hdr_file="B.h",cxx_hdr_files="A.h"> {
module Bmod {
Aobject &my_input;
};
};

This example creates a dependency between Bmod and Aobject. The library B must specify the cxx_hdr_files property so that B.h knows to include A.h before it proceeds.

Determining such a dependency is especially tricky if you have specified the build_dir property on the object that has the out_hdr_file property set. In this case, the file specified by out_hdr_file is placed into the build_dir directory. In order to reference the header file, you must prepend the value of build_dir onto the value of out_hdr_file in the cxx_hdr_files definition. For example:

library A<build_dir="ADIR",out_hdr_file="A.h"> {
group Aobject {
};
};
library B<out_hdr_file="B.h",cxx_hdr_files="ADIR/A.h"> {
module Bmod {
Aobject &my_input;
};
};
Importing Existing C++ Classes

You can import an existing C++ class into AVS/Express. One way to do this is as follows:

1. Manually instance the C++ class when your AVS/Express object is instanced.
2. Store a pointer to the C++ object with the AVS/Express object.
3. Delete the C++ object when the AVS/Express object is deinstanced.

However, this process requires significant programming effort and breaks down when the AVS/Express object and the C++ class are subclassed. Therefore, AVS/Express provides a mechanism to automate the import process.

1. Associate the class name of a C++ object with the AVS/Express module by setting the cxx_class property to contain the name of the C++ class.
When the AVS/Express C++ object is instanced or deinstanced, AVS/Express automatically creates an instance of the C++ object or destroys the instance. When a cxxmethod is called, you can get a pointer to the C++ object.
2. Ensure that AVS/Express can find the C++ class by setting the cxx_hdr_files and cxx_src_files properties either on the module or the library containing the module. These properties should reference the file (or list of files) needed to define the C++ class.
3. Once in the update method, get a pointer to the C++ object with the ret_class_ptr method. This method is called with a single argumentthat is the string name of the class passed to cxx_class. It returns the following value, which you can cast into a pointer of the correct type:
"void *"

Suppose, for example, that you have a C++ class that implements a quadratic equation solver, and that the header for this class, quad.hxx, reads as follows:

class QuadraticSolver {
protected:
float a, b, c;
public:
void set_params(float new_a,float new_b,float new_c);
virtual int get_roots(float &r1,float &r2);
};

The source for this class might be the following, in quad.cxx:

#include <math.h>
#include "quad.hxx"
void
QuadraticSolver::set_params(float new_a, float new_b, float new_c)
{
a = new_a;
b = new_b;
c = new_c;
}
int
QuadraticSolver::get_roots(float &r1, float &r2)
{
float op = b * b - 4 * a * c;
if (op < 0) return(0); // roots are imaginary
   op = sqrt(op);
   r1 = (-b + op) / (2 * a);
r2 = (-b - op) / (2 * a);
   return(1);
}

You define the interface to this object using the following V code:

module EXquad_solver<cxx_class="QuadraticSolver",
    cxx_hdr_files="quad.hxx",
    cxx_src_files="quad.cxx",
    src_file="quad_mod.cxx"> {
float+read+notify+req a, b, c;
float+write r1, r2;
string+write status;
cxxmethod+req update_roots;
};

The source for this method then looks like the following:

int EXquad_solver::update_roots(OMevent_mask event_mask,
  int seq_num)
{
// Get the pointer to the user defined C++ class
QuadraticSolver *my_class_ptr =
(QuadraticSolver *)ret_class_ptr("QuadraticSolver");
my_class_ptr->set_params(a, b, c);
//
// must use temps since r1 and r2 are OMXfloats, not floats
// and the get_roots method takes references to floats.
//
float r1_tmp, r2_tmp;
if (my_class_ptr->get_roots(r1_tmp, r2_tmp) == 0) {
status = "roots are imaginary";
r1 = r2 = 0;
}
else {
status = "roots are real";
r1 = r1_tmp;
r2 = r2_tmp;
}
return(1);
}
Constructing User Classes

It is possible for the constructor to a C++ class specified with the cxx_class property to take arguments. In this case, you use the cxx_class_args property to specify the parameter list to the constructor. The cxx_class_args property can reference variables defined as parameters to your AVS/Express object.

The cxx_class_args property takes a string that is placed between the parentheses of the new operator in C++. You can supply any valid C++ code that can be used in this context. Suppose, for example, that the constructor for the QuadraticSolver class in the previous example takes default values for the parameters a, b, c:

class QuadraticSolver {
protected:
float a, b, c;
public:
QuadraticSolver(float a, float b, float c);
void set_params(float a,float b,float c);
virtual int get_roots(float &r1,float &r2);
};

Suppose also that the constructor simply initialized the values of a, b, and c, as follows:

QuadraticSolver::QuadraticSolver(float init_a,
   float init_b,
   float init_c)
{
a = init_a;
b = init_b;
c = init_c;
}

You specify the V code for this object as follows:

module EXquad_solver<cxx_class="QuadraticSolver",
    cxx_class_args="a,b,c",
    cxx_hdr_files="quad.hxx",
    cxx_src_files="quad.cxx",
    src_file="quad_mod.cxx"> {
float+read+notify+req a, b, c;
float+write r1, r2;
string+write status;
cxxmethod+notify_inst+req update_roots;
};

The generated code that uses the cxx_class_args property then looks like the following:

void
EXquad_solver::init_class_ptr()
{
class_ptr = (void *)new QuadraticSolver(a,b,c);
}
Defining Abstract User Classes

When you associate a C++ class with an AVS/Express object using the cxx_class property, AVS/Express generates code to instance your class. If you define an abstract class (that is, a class that cannot be instanced because its constructor is private), the generation of this code causes an error. To work around this problem, set the cxx_abstract property to 1, which instructs AVS/Express not to generate code that tries to construct your object.

Adding Your Own Member Variables

You can use the cxx_members property to add your own member variables to a generated C++ class. This property takes a string value that is placed directly into a public section of the generated C++ class. This code can then declare new member variables that the C++ code associated with the cxxmethod can reference. For example, suppose that you write the following V code to add a char * value that only your code maintains:

module my_module<cxx_members="char *my_ptr;"> {
...
cxxmethod+notify update;
};

You can ensure that this variable is declared in a private section by adding the following:

module my_module<cxx_members="private: char *my_ptr;"> {
...
cxxmethod+notify update;
};

In addition, you can add code to construct these members with the property cxx_members_constr. The string for this property is inserted in the code that defines the constructor for the generated C++ class. To initialize the pointer to 0, use the following V code:

module my_module<cxx_members="private: char *my_ptr;",
cxx_members_constr="my_ptr(0)"> {
...
cxxmethod+notify update;
};
Exporting C++ Classes

AVS/Express automatically generates a C++ class definition for each object that contains one or more cxxmethod subobjects. In addition to using this generated class to implement the cxxmethod, you can use it to create an instance of the object programmatically. This is convenient particularly given the limitation in AVS/Express that each object can have only a single C++ class associated with it. (This is not a severe limitation since you can easily make a derived copy associate a different C++ class with an identical object.)

To force AVS/Express to generate a C++ class for a particular object, use the export_cxx property. This property specifies one of four modes that control whether and how the C++ class is generated for this object:

For example,suppose that you have the following V code object definition:

macro my_macro {
group my_module {
   int my_exported_param<export=3>; // exported to my_macro
   int my_hidden_param<export=0>; // hidden to everyone..
   int my_param; // gets the default
};
};

The following examples illustrate how AVS/Express generates a C++ class in the modes 1, 2, and 3.

Mode 1

class my_module ... {
OMXint my_exported_param;
OMXint my_param;
};
class my_macro ... {
my_module my_module;
};

Mode 2

class my_module ... {
OMXint my_exported_param;
};
class my_macro ... {
my_module my_module;
};

Mode 3

class my_macro ... {
OMXint my_exported_param; // moved up to top level
};

A caveat when using mode 3: because an exported subobject is promoted to the top level, V does not guarantee that its name is unique. If a conflict occurs, the C++ compiler gives a syntax error. You can use the cxx_name property on one or more subobjects to specify an alternate name to use when generating either C++ class name or the C++ member name for the object.

Recall that modes 1 and 2 preserve the AVS/Express class hierarchy in the generated C++ classes. This means, for example, that if AVS/Express object B is derived from AVS/Express object A, the C++ class that corresponds to object B is derived from the C++ class that corresponds to object A. Suppose that you have the following V code definition:

group A<export_cxx=1>{
};
A B<export_cxx=1>;

For this definition, AVS/Express generates the following C++ class hierarchy:

class A : public virtual OMXgroup {
...
};

class B : public virtual OMXgroup, public A {
...
};

If you set export_cxx to 1 or 2, you can treat an object of class B as an object of class A. However, if the value of export_cxx is 3, the C++ class hierarchy does not correspond to the AVS/Express object derivation hierarchy.

Note: If a cxxmethod is associated with the AVS/Express object, AVS/Express uses a value of 1 for export_cxx when generating the C++ class unless you explicitly specify a value for this property.

You can set the export_cxx property only on hierarchical objects: libraries, macros, modules, groups, or applications. If you set this property on a library, it does not generate a C++ class for the library but generates C++ classes for all subobjects of the library (until the export_cxx property is set again to override the parent's value).

Here are some important rules to follow when generating C++ classes for a library of objects:

Example of Generated C++ Classes

This example illustrates using the C++ class export mechanism to build a C++ application that uses AVS/Express objects. The application is a simple isosurface/isoline viewer. The C++ program inputs data, selects a contour level, and displays the output using a viewer object.

You start by selecting the objects that the application needs. For this particular application, you collect them into a single library called EXiso_objects and give them new unique names. This makes it possible to customize them without changing the names referenced by the C++ code. It also prevents conflicts with other applications that might create C++ classes for standard AVS/Express objects.

Here is the V code for this project:

library EXiso_objects<
  export_cxx=3,
  out_hdr_file="ex_out.h",
  out_src_file="ex_out.cxx",
  process="ex_iso",
  no_main=1, // ex_iso.cxx has its own main...
  cxx_hdr_files="fld/Xfld.h",
  cxx_src_files="ex_main.cxx"> {
SimpleViewer3D EXviewer {
   View3D.View.back_col<export=4>;
};
library EXdv_objects<export_cxx=1> {
   DV.DViso EXiso;
   DV.DVext_edge EXext_edge;
};
VIEW.DataObject EXdata_object;
FLD_MAP.uniform_scalar_field EXuniform_scalar_field;
};

This code sets the following values on the EXiso_objects library:

The following objects are defined in the EXiso_objects library:

The two objects are as follows:
  • EXiso. This object creates isolines for a 2D input field and an isosurface for a 3D input field.
  • EXext_edge. This object shows the boundaries of the cells for the input field.

Here is the source code for this application:

// Include the header file set with out_hdr_file on the library.
#include "ex_out.h"
void
main (int argc, char **argv)
{
// Causes the system to not try and load "inst.v" by default.
OMset_init_state(OM_INIT_LOAD_TEMPL);

// Initialize the system (load the templates).
(void) OMmain_init(argc, argv);

// Create a new application object to house the objects.
// This makes it more convenient to debug in the Network Editor.
OMXappl *appl = new OMXappl;

// Begin a single operation... no results will be displayed until
// the correspond pop_ctx method is invoked.
appl->push_ctx();

// Create the objects, placing them in the application.
EXuniform_scalar_field *field =
new EXuniform_scalar_field(*appl);
EXiso *iso = new EXiso(*appl);
EXext_edge *ext_edge = new EXext_edge(*appl);
EXdata_object *obj1 = new EXdata_object(*appl);
EXdata_object *obj2 = new EXdata_object(*appl);
EXviewer *viewer = new EXviewer(*appl);

// Connect them: explicit casts to OMobj_id are necessary.
iso->in.set_obj_ref((OMobj_id) field->out);
ext_edge->in.set_obj_ref((OMobj_id)field->out);
obj1->in.set_obj_ref((OMobj_id)iso->out);
obj2->in.set_obj_ref((OMobj_id)ext_edge->out);
viewer->objs_in.add_obj_ref((OMobj_id)obj1->obj);
viewer->objs_in.add_obj_ref((OMobj_id)obj2->obj);

// Specify the dimensions of the field that we are creating.
int dims[2];
dims[0] = dims[1] = 4;
field->in_dims.set_array(OM_TYPE_INT,(char *)dims,2,
     OM_SET_ARRAY_COPY);

// Specify data for the field that we are creating.
static int data[16] = {0,1,3,4, 6,3,2,1, 5,2,1,4, 3,1,5,4};
field->in_data.set_array(OM_TYPE_INT,(char *)data,16,
     OM_SET_ARRAY_COPY);

// Set the isosurface/line threshold value and the edge angle.
iso->level = 2.5;
ext_edge->angle = 0;

// Modify the background color of the view window.
float *back_col =
viewer->back_col.ret_array_ptr(OM_GET_ARRAY_WR);
back_col[0] = 0.3;
back_col[1] = 0.3;
back_col[2] = 0.3;
ARRfree((char *)back_col);
appl->pop_ctx(); // Causes refresh.

OMmain_loop(OM_VCP_DEFAULT); //OM_VCP_SUPPRESS to get rid of vcp.
}
9.5 The FORTRAN API: Overview

The FORTRAN API includes two types of routines, identifiable by their prefixes as noted in the following table:

Table I-1
Type
FORTRAN prefix
Array allocation/deallocation routines
ARRF
Object Manager manipulation routines
OMF

The AVS/Express online help system provides reference descriptions of the FORTRAN routines. Use these descriptions as your primary source of information (see the next section for notes on how to use the descriptions). To supplement these descriptions, this section provides a summary of the available routines and information on header files, and a subsequent section provides usage notes for the FORTRAN API routines.

Using the Online Help System Descriptions

The AVS/Express online help system provides a separate description for each FORTRAN routine (related routines are not consolidated under the same description, as for C and C++). For conciseness, the descriptions of most FORTRAN routines consist only of the following:

A few descriptions describe functionality specific to FORTRAN and do not, therefore, reference C API descriptions.

FORTRAN API Routines

This section summarizes the FORTRAN Object Manager API routines. The tables in this section group the routines by functional category, to help you find the routine you need to accomplish a particular task. Within functional groupings, the routines are grouped as follows:

Note that unlike for the C and C++ routines, these groupings do not reflect the organization of the FORTRAN descriptions in the online help system; they are used simply to make locating related routines easier. Always look for the online help system description of a FORTRAN API routine under its own name.

Getting and Setting Object IDs
Table I-2
Routine
Description
OMFstr_to_name
Converts a string to an object name
OMFname_to_str
Converts an object name to a string
OMFfind_subobj
OMFlookup_subobj
OMFfind_obj
Returns the ID for an specified object
OMFget_obj_parent
Gets the ID of an object's parent
OMFequal_objs
Compares two object IDs
OMFis_null_obj
Determines whether an object ID is null
OMFget_array_val
OMFset_array_val
OMFdel_array_val
Gets, sets, or deletes an object ID in a one-dimensional array object (the object is either an array of pointers/references to other objects or a by-value group array)

Getting and Setting Object Definitions
Table I-3
Routine
Description
OMFget_data_type
OMFset_data_type
Gets or sets an object's data type
OMFget_obj_name
OMFset_obj_name
Gets or sets an object's name

Getting and Setting Scalar Object Values
Table I-4
Routine
Description
OMFget_int_val
OMFset_int_val
OMFget_name_int_val
OMFset_name_int_val
Gets or sets an integer object's value
OMFget_real8_val
OMFset_real8_val
OMFget_name_real8_val
OMFset_name_real8_val
Gets or sets a floating-point object's value
OMFget_str_val
OMFset_str_val
OMFget_name_str_val
OMFset_name_str_val
OMFget_str_val_mode
Gets or sets a string object's value

Getting and Setting Array Object Values
Table I-5
Routine
Description
OMFret_array_ptr
OMFret_name_array_ptr
OMFret_typed_array_ptr
Returns the value of an array (used for getting and setting an array object's value)
OMFget_array
Gets or sets the value of an array
ARRFalloc
Allocates space for an array object
ARRFfree
Frees an array object's space
ARRFincr_refcnt
Increments the reference count on an array allocated with ARRFalloc
OMFget_array_dims
Gets an array object's dimensions
OMFget_array_size
OMFset_array_size
Gets or sets an array object's size
OMFget_str_array_val
OMFset_str_array_val
Gets or sets an element in a one-dimensional string-array object
OMFget_sub_iarray
OMFset_sub_iarray
OMFget_sub_farray
OMFset_sub_farray
OMFget_sub_rarray
OMFset_sub_rarray
Gets or sets a subarray of an array object
OMFset_array
Sets an array object (that is, all elements in the array object)
ARRFoffset
Accesses memory locations specified by a pointer in a portable manner
ARRFretptr
Returns the address of a local memory location (required for passing locally defined arrays to AVS/Express)

FORTRAN API Header Files

The FORTRAN header file omf.inc contains predefined values specified via PARAMETER statements and function type declarations. Most functions are of type INTEGER but do not follow implicit type declaration conventions. It is important, therefore, to include the header file omf.inc whenever your code calls FORTRAN API routines.

A typical way to include this file is to use the following preprocessor statement:

#include <avs/omf.inc>

An SGI f77 machine calls the preprocessor automatically; on Sun and HP machines, the filename extension ".F" is required to resolve the statement correctly.

Alternatively:

9.6 FORTRAN API Usage Notes

The FORTRAN API provides a subset of the calls in the C API for the operations most commonly used to write modules. These routines are implemented by wrappers that call the equivalent C API routine. The FORTRAN API is implemented with a library called the OMF library, and all of its routines are prefixed with the letters OMF. This section defines some basic concepts used by this library and provides a high-level description of basic operations using the routines in this library.

For detailed descriptions of the FORTRAN API routines, see the AVS/Express online help system.

Object Manager Data Structures

The Object Manager provides special data structures for two special types of data that it references:

Object IDs

AVS/Express assigns a unique ID to each object. In a C function, the type of an object ID is OMobj_id (see Object IDs on page 9-16); the FORTRAN API represents this type as the obj_id type, an array of 32-bit integers, by default of size 2. The omf.inc header file defines the size of this type via the parameters OM_OBJ_ID_SIZE or OIDSIZ, which you should use; for example:

integer obj_id(OIDSIZ)

Almost all routines in the OMF library operate on the object ID data structure. This type is network transparent; that is, it can be passed from process to process, and machine to machine without change.

The omf.inc header file also provides the following set of predefined object ID variables, for use in your application:

Table I-1
Variable name
Description
OMinst_obj
The ID of the Applications object
OMnull_obj
A null object ID
OMroot_obj
The ID of the Root object
OMtempl_obj
The ID of the Templates object

These objects are defined in the file as follows:

      INTEGER OMnull_obj(OIDSIZ),
      INTEGER OMroot_obj(OIDSIZ),
      INTEGER OMinst_obj(OIDSIZ),
      INTEGER OMtempl_obj(OIDSIZ)
      COMMON /OMF/ OMnull_obj, OMroot_obj, OMinst_obj, OMtempl_obj

By default, their values are not initialized. You can set them as follows:

istat = OMFstd_objs_set (OMnull_obj,
OMroot_obj,
OMinst_obj,
OMtempl_obj)
Object Names

Each object in an AVS/Express application has a name. One representation of this name is a name string: a character string that is unique among its sibling objects in the object hierarchy. The Object Manager does not store and manipulate this name string directly; instead, it allocates a unique integer value that maps directly to the name string. In a C function, the type of an object name is OMobj_name (see Object Names on page 9-18); the FORTRAN API represents this type as an INTEGER type. This approach has two primary benefits:

Converting Between Integer-Value Object Names and Name Strings

The FORTRAN API contains routines that you can use to

The C API also includes routines that you can use to convert between an integer-valueobject name and the corresponding name string.

For example, you can get an integer-value object name from a name string as follows:

      integer name
      name = OMFstr_to_name("astring")

To start with the integer-value object name and get the name string:

      integer name
      character*20 name_str
C
      name = OMFstr_to_name("string")
      idum = OMFname_to_str(name, name_str)

You should treat the name string returned from OMFname_to_str as read-only storage. Do not reuse the memory to which it points.

Comparing Integer-Value Object Names

To compare two integer-value object names for equality, use the FORTRAN equality operator (EQ=).

FORTRAN Arrays

AVS/Express supports the full array access mechanism available in the C API for FORTRAN arrays of type INTEGER, REAL, and DOUBLE PRECISION, thereby conserving the memory organization of an array. For multi-dimensional arrays specified in AVS/Express as the following:

float xyz[n1][n2];

The corresponding FORTRAN representation is the following:

REAL xyz(n2,n1).

Some C API routines expect or return pointers to memory locations. For efficiency reasons, the FORTRAN API requires a corresponding mechanism. Since FORTRAN77 does not have a standard (and therefore portable) pointer type, the address of a memory location is represented in terms of an INTEGER type. In cases where the address of an allocated memory area is returned, a function for the translation into an offset relative to a specified memory location is provided. Another utility function returns the address of a storage area for the purpose of passing a FORTRAN array location to the Object Manager.

Note: Array indexing in AVS/Express is zero-based, that is, the first element of an array object has the index 0. This is important when addressing particular array elements or ranges via an Object Manager API function.
Object Manager Return Values

With few exceptions, OMF routines return an integer status code. It can have the following values, which are defined in the omf.inc header file:

Table I-2
Status code
Value
Meaning
OM_STAT_SUCCESS
1
The routine completed successfully.
OM_STAT_UNDEF
0
The routine tried to get a value from an object that has no defined value.
OM_STAT_ERROR
-1
The routine failed for some other reason.

If a routine does not return one of these values, the description of the routine in the online help system explicitly states the nature of the its return value.

Using a Mode Argument

Many OMF routines take a mode argument as their last argument. This mode argument alters the behavior of the routine: it specifies a list of flags that are combined using the logical OR operation.

For a list of mode argument values for a specific OMF routine, see its description in the online help system.

To use a routine's default behavior (that is, no optional modes), specify a mode value of 0. Note that a mnemonic constant that corresponds to this mode value may not exist.

Accessing the Object Hierarchy

One of the main operations performed in AVS/Express is traversing the AVS/Express object hierarchy to look for a specific object. Using a single set of traversal routines, you can find objects in a library, parameters in a module, modules in a macro, and so forth.

Finding Subobjects by Name

If you know the name of the subobject that you want to access, you can use either OMFfind_subobj or OMFfind_str_subobj to find it. You use these routines as follows:

      integer
      OMFfind_subobj(par_id, name, mode, child_id)
      integer par_id(OIDSIZ), name, mode, child_id(OIDSIZ)

      integer
      OMFfind_str_subobj(par_id, name_str, mode, child_id)
      integer par_id(OIDSIZ)
      character*256 name
      integer mode, child_id(OIDSIZ)

Each of these routines returns a subobject of the object par_id in the argument child_id.

The mode argument. If you cannot guarantee that changes will not be made to the object whose ID is returned, specify OM_OBJ_RW as the mode argument for both of these calls. If you can make this guarantee, specify OM_OBJ_RD instead. The value OM_OBJ_RD can prevent the creation of subobjects that are inherited from a base template. In this case the returned object has the base template as a parent, not the object specified with parent_id. When in doubt, use the flag OM_OBJ_RW.

The returned subobject. OMfind_subobj returns the ID of an immediate subobject of the object specified by par_id with the name specified by the name parameter. For example, if you are implementing an update method for a module, you can use the OMFfind_subobj routine to get the object ID of one of your parameters:

      integer function update(obj_id, mask, seq_num)
      include 'avs/omf.inc'
      integer obj_id(OIDSIZ), mask, seq_num, name
      integer param_id(OIDSIZ)(
C
      update = 0
      name = OMFstr_to_name('num1')
      if (OMFfind_subobj(obj_id,name,OM_OBJ_RW,param_id) .NE. 1)
      & return
Traversing Subobjects as a List

By comparison, OMFfind_str_subobj can take a pathname to the subobject. This pathname can have multiple levels of path entries and can go up levels in the hierarchy with the <- notation. If the pathname argument, name, has [n] specified, it can also go through array values. The following are examples of valid string values for the pathname argument:

      if (OMFfind_str_subobj(parent_id,`A.B.C',
     &         OM_OBJ_RW,ret_id) .NE. 1) return

If these OMF routines do not fine the specified subobject or another error occurs, they return a status of OM_STAT_UNDEF (0).

Determining When Parameters Change

A module written with an fmethod (that is, a module whose method is implemented by a FORTRAN subroutine) has a sequence number parameter as the third argument of its callback function. To determine whether any parameter has been modified since the last time this method ran, specify this sequence number as the argument to the function; for example:

      integer function OMFchanged(param_id, seq_num)
      integer param_id(OIDSIZ)
      int seq_num

OMFchanged returns the following integer status values: 1 to indicate that the parameter has changed, 0 to indicate that it has not, and -1 to indicate an invalid parameter_id argument.

Setting and Getting Data Values

The FORTRAN API provides routines to set and get all data values that can be maintained using AVS/Express objects. There are several rules for how these routines generally operate.

The first argument to the basic OMF routines that set and get data values is the object ID (obj_id) of the primitive data object. For example, you specify the OMF routine that gets an integer value from an object as follows:

      integer function OMFget_int_val(obj_id, ret_val)
      integer obj_id(OIDSIZ), ret_val

If OMFget_int_val returns an error, the ret_val integer is not changed.

Before you can use this routine from a module, you must get the object ID of the parameter from the object ID of the parent, using the OMFfind_subobj routine. For example, suppose that the V code for your module is as follows:

module my_module {
int p1;
fmethod+notify+read update = "my_module_update";
};

The FORTRAN source code for the my_module_update subroutine is as follows:

      integer function my_module_update(obj_id, mask, seq_num)
      include 'avs/omf.inc'
      integer obj_id(OIDSIZ), mask, seq_num, name
      integer param_id(OIDSIZ), p1_val
C
      my_module_update = 0
      name = OMFstr_to_name('p1')
      if (OMFfind_subobj(obj_id,name,OM_OBJ_RW,param_id) .NE. 1)
     & return
      if (OMFget_int_val(param_id, p1_val) .NE. 1)
     &   return
C
C operate on p1_val here
C
         my_module_update = 1;
      return

For many data set and get routines, the OFM library provides a convenience version that performs both the OMFfind_subobj and OMFget_int_val calls in one step. For OMFget_int_val, the routine is called OMFget_name_int_val. It is specified as follows:

      integer function OMFget_name_int_val(obj_id,sub_name,ival)
      integer obj_id(OIDSIZ), sub_name, ival

Using this convenience routine, you can write the following more succinct FORTRAN code for my_module_update:

      integer function update(obj_id, mask, seq_num)
      include 'avs/omf.inc'
      integer obj_id(OIDSIZ), mask, seq_num, name
      integer param_id(OIDSIZ), p1_val
C
      update = 0
      name = OMFstr_to_name('p1')
      if (OMFget_name_int_val(param_id, name, p1_val) .NE. 1)
     &   return
C
C operate on p1_val here
C
         update = 1;
      return
Scalar Primitive Values

AVS/Express supports getting and setting the following types of scalar primitive values:

data type
get routine
set routine
int, byte, short, char
OMFget_int_val
OMFset_int_val
float, double
OMFget_real8_val
OMFset_real8_val
string
OMFget_str_val
OMFset_str_val

Getting and Setting Real Values

AVS/Express always returns real numbers in real*8 format, even if they are stored as float objects. Therefore, a call to the OMFget_real8_val routine looks like the following:

      real*8 rval
      if (OMFget_real8_val(obj_id, rval) .NE. 1) return
Getting and Setting String Values

The routine OMFget_str_val is declared as follows:

      integer function OMFget_str_val(obj_id, retstr, maxlen)
      integer obj_id(OIDSIZ)
      character*?? retstr
      integer maxlen

If OMFget_str_val finds the object specified by obj_id, it returns its string value in the parameter retstr. (The string that you pass to the routine should be of size maxlen.) If obj_id is either unset or undefined, the routine returns the standard error code.

Here is an example of how to call this routine:

      character*80 strval
C
      if (OMFget_str_val(obj_id, strval, 80) .NE. 1) return
Array Primitive Values

The FORTRAN API supports getting and setting the following types of primitive arrays:

These are accessed with different OMF routines.

Getting and Setting String Arrays

String arrays have two limitations:

To determine the size of a string array, use the routine OMFget_array_size. To get a specific string value, use the routine OMFget_str_array_val. To set a particular string value, use the routine OMFset_str_array_val.

Getting and Setting Raw Data Arrays

You can either access raw data arrays in their entirety or process them as subarrays; that is, a piece at a time.

See Accessing Entire Arrays on page 9-87 for details.

Accessing Subarrays

When operating on arrays a piece at a time, you specify the range of each dimension that you want to set or get, then specify a piece of memory of the appropriate size for the operation. You then copy the data either into or out of this memory using the appropriate OMFget_sub_xarray or OMFset_sub_xarray routine. AVS/Express provides a different set of these routines for each data type it supports, as follows:

Table I-3
Data type
Get routine
Set routine
integer
OMFget_sub_iarray
OMFset_sub_iarray
real
OMFget_sub_farray
OMFset_sub_farray
real*8
OMFget_sub_rarray
OMFset_sub_rarray

To perform a sub_array call (either set or get) you must provide three pieces of information:

For example, here is the declaration for the OMFset_sub_iarray routine:

      integer function OMFset_sub_iarray(obj_id, ndim, dims,      &                   min_rng, max_rng, array)
      integer obj_id(OIDSIZ), ndim, dims[OM_ARRAY_MAXDIM]
      integer min_rng[OM_ARRAY_MAXDIM], max_rng[OM_ARRAY_MAXDIM]
      integer array[*]

The declarations for the other OMFget_sub_xarray or OMFset_sub_xarray routines are the same except for the data type of the array pointer argument. In these declarations:

(max_rng[0] - min_rng[0]) *
(max_rng[1] - min_rng[1]) *
...
(max_rng[ndim-1] - min_rng[ndim-1])
For OMFset_sub_xarray routines, this memory is copied into the appropriate locations of the AVS/Express array object. For OMFget_sub_xarray calls, AVS/Express returns the requested values in this memory.
Accessing Entire Arrays

How you access an entire array depends on whether you know the dimensions of the array before you begin to operate on it.

Array dimensions are known. When the dimensions of the array are defined in the AVS/Express object before you begin the operation (which must always be true for read-only array operations), you can have AVS/Express return a pointer to an array that it has allocated that you can then operate on. This technique works for all modes of array operations: read-only, write-only, and read-write. You use the following OMF routines:

Table I-4
Routine
Common usage
OMFret_array_ptr
Returns pointer, size, and type
OMFret_typed_array_ptr
Returns pointer and size, forces array to specified type
OMFget_array
General routine for advanced usage

Each of these routines takes an array mode that specifies how you will access the memory pointer returned to you. Four modes are available:

Because these routines are not intended to copy potentially large arrays and because FORTRAN does not support pointers, they use an offset mapping scheme to allow you to index the returned array. The routine returns an integer offset that refers to the array. You call the routine ARRFoffset with this offset and a dummy array of your own. The ARRFoffset routine returns an offset that you add to the index of your array to dereference the array.

Note: This technique will not work if your compiler enforces array bounds checking.

After using these routines, be sure to free the array by calling ARRFfree. Calling this routine serves two purposes: it prevents memory leaks and it signals that you are finished with the array. If you specified the mode as OM_GET_ARRAY_WR or OM_GET_ARRAY_RW,a call to ARRFfree also queues the events of any methods that requested notification on the array.

The following example illustrates use of the OMFret_array_ptr routine:

      integer ndims, dims(OM_ARRAY_MAXDIM), iaddr1, offset1
      real base(1)
C
C Get the "integer address" of the array
C
      iaddr1 = OMFret_array_ptr(obj_id,OM_GET_ARRAY_WR,isz,itype)
C
C Get the offset from this address to our `base' array
C
      offset1 = ARRFoffset(iaddr1, base, OM_TYPE_FLOAT)
C
C Now add this offset onto the normal loop index for our array
C
      do 101 i=1,isz
         base(offset1 + i) = 333.333
 101  continue
C
C Free the array when we are done with it
C
      ARRFfree(iaddr1)

Array dimensions are not known. When the dimensions of an array are not defined in the AVS/Express object before you begin to operate on it, use the OMset_array routine. This routine provides flexibility in how AVS/Express allocates and frees the array. You first define the array using memory that you manage and then call OMFset_array to tell the AVS/Express object about the new value for the array. At this time, events are queued for any methods that have requested notification on the array.

OMset_array takes an array mode that specifies how the memory passed in for this array is treated. Three modes are available:

For example:
      real values(3)
      values(0) = 0
      values(1) = 1
      values(2) = 2
      OMFset_array(obj_id, OM_TYPE_FLOAT, values, 3,      &             OM_SET_ARRAY_COPY)
For example:
      integer iaddr, offset, i
      real base(1)
      iaddr = ARRFalloc(0, OM_TYPE_FLOAT, 10, 0)
      offset = ARRFoffset(iaddr, base, OM_TYPE_FLOAT)
      do 101 i = 1, 10
         base(i+offset) = 2 * i
 101  continue
One way that this routine can be used is to have AVS/Express point to an array in a FORTRAN common block. You can subsequently modify the array but you must then issue another call to OMFset_array with the same pointer so that AVS/Express can notify methods that may depend upon this array.
Returning Object Values to the Unset State

The basic primitive set commands do not provide a mechanism for deleting the value of an object and returning it to the unset state. To do this, you can call OMFset_obj_value as follows:

      OMFset_obj_val(obj_id, OMnull_obj, 0)

This call also breaks any existing connections that this object may have.



TOC PREV NEXT INDEX