TOC PREV NEXT INDEX

Using AVS/Express




8 Developing Modules


This chapter describes how to create and manage modules. It includes the following sections:

8.1 Introduction

A module is an AVS/Express object that contains parameters and methods.

You can develop user modules for use in AVS/Express applications either by adding new, custom modules or by modifying existing AVS/Express modules into custom modules.

Note: Many examples in this chapter are written using the V command language. For a description, see Chapter 7, V and the V Command Processor.
Adding New Modules

AVS/Express provides two tools for adding new modules:

Modules run by executing functions written in C, C++, or FORTRAN. The execution of these functions is supported by a third tool: the Object Manager APIs, which are available in C, C++ , or FORTRAN versions (see Chapter 9, Object Manager APIs for details).

The Add Module Tool

The Add Module Tool is a series of dialog boxes that guide you though the steps you need to complete in order to add your own module to AVS/Express. The dialog boxes provide a structured interface that make it easy to set the necessary properties for the module and the methods and parameters it contains. You can use the Add Module Tool in two ways:

1. You can create a module that encapsulates code you have already written and integrate it into AVS/Express. You can then connect the module into an AVS/Express network and use it with other objects.
2. You can build a module using the Add Module Tool and then ask AVS/Express to generate code for it. AVS/Express uses the structure you have created in the Network Editor and generates skeleton code describing it. You can then edit the code to function correctly.
Note: To integrate your own code into AVS/Express, you must create your own project and work in it. For introductory information about projects, see Saving Your Work In Projects on page 3-36.

The Add Module Tool leads you through the following three steps to create a module:

1. Name your module and define one or more methods for it. You must create at least one method before adding parameters.
2. Define parameters for the module and the interaction between the parameters and methods.
3. Specify the code management properties that control how your code is integrated into AVS/Express.

For more information about the Add Module Tool see the Getting Started manual for usage details, and the on-line help for panel details.

Modifying Existing Modules

You can modify existing modules using the Object Editor or the V command language. Just as for new modules, support is also provided by the Object Manager APIs.

For more information, see Chapter 6, Managing and Editing Objects, Chapter 7, V and the V Command Processor, or Chapter 9, Object Manager APIs, respectively.

Any code running in the AVS/Express environment can create, modify, or destroy instances of AVS/Express objects, including code in the following:

All three Object Manager APIs allow this flexibility.

8.2 Module Overview

As noted previously, a module contains parameters and methods. For example:

module AddImage {
Image+read+notify &Image1;
Image+read+notify &Image2;
Image+write outImage;
omethod+notify_inst update = "AddImage_update";
};

In this module, Image1, Image2, and outImage are parameters and update is a method.

Creating a custom module-whether as a new module or a modification of an existing module-involves setting up its methods and parameters and defining how they interact. Specifically, you must:

This section provides an overview of parameters and methods and how to relate them. Subsequent section provides detailed information on setting up target functions for your methods, and information on some special module-level topics.

Parameters

Parameters define various aspects of a module's operation. A parameter can be a primitive data object (int, float, byte, and so on) or a group data object (any object whose base type is group). In some cases, a parameter may even be another module.

Methods

Methods access and can modify parameter values. Each method in a module is associated with a specific target function-written in C, C++, or FORTRAN-that is executed when the method is triggered. Typical trigger events include instancing (creating) or deinstancing (destroying) the module or changing a parameter value. You can have different events trigger the same method: methods provide flags that indicate which event triggered the method. Also, when AVS/Express executes a method, you can check which parameters of the module have changed since the method was last executed.

Method Types

There are three types of methods, corresponding to the three languages supported for target functions. The following table notes the method types, their associated languages, and their values (the value of a method is the name of its target function).

Table H-1
Method type
Target function language
Method value
omethod
C
A string value that specifies the name of the function
cxxmethod
C++
An optional string value that is interpreted as source code for the method (see Specifying the cxxmethod Type on page 8-18 for details)
fmethod
FORTRAN
A string value that specifies the name of the function

For detailed information on setting up a module to use a particular type of target function, see Target Functions on page 8-15.

Naming Methods

When you add a custom module that interfaces to C, C++, and/or FORTRAN code, AVS/Express generates additional code that binds the module to your code. This generated code uses symbols derived from the names of AVS/Express objects (for generating C++ classes) and the string values of AVS/Express method objects (omethod and cxxmethod).

You must exercise caution when naming the target functions for your methods, in order that they do not clash with symbols defined by other components that you are using. To do this, simply add a unique prefix to the name of target function. As a general rule, avoid the following:

If your names do cause conflicts, AVS/Express returns errors when it compiles the generated code.

Setting Method and Parameter Attributes

When you define a method, you must set various attributes that control what events trigger the method and how the method interacts with the associated parameters.

Setting Method Attributes

As noted previously, the instancing and/or deinstancing of a module is a common trigger event for the module's method. To specify that this occurs, you merge notify_inst or notify_deinst attributes with the relevant method:

In this example, instancing the containing module always triggers the update method:

omethod+notify_inst update = "AddImage_update";

Another common trigger event is a change in a parameter value. This is handled with the notify/nonotify parameter attributes rather than with method attributes (see the next section for details).

For a discussion of how event notification works, see Event Notification of Methods on page 8-11.

Setting Parameter Attributes

Whenever you define a method, you must set some basic parameter attributes that specify how the method interacts with the associated parameters. At minimum, you must specify settings for the read/noread, write/nowrite, notify/nonotify, and req attributes.

For descriptions of all of the module-control attributes you can set, see Module-Control Properties and Attributes on page 14-12.

The following list explains how to use these attributes.

How you set these attributes determines the execution order of methods when the same operation triggers more than one method. If Method A reads a parameter and Method B writes the same parameter, Method B is executed first. (For an example, see Controlling Method Execution Order on page 8-13.)
If neither read nor noread is set on a particular object, it inherits the read/noread setting of its parent object. Similarly, if neither write nor nowrite is set on a particular object, it inherits the write/nowrite setting of its parent object.
If you set the notify attribute on a parameter for a particular method, changing the parameter's value queues an event to trigger the method. The event is delivered when the current operation is complete.
If neither notify nor nonotify is set on a particular object, it inherits the notify/nonotify setting of its parent object.
Another common trigger event is the instancing/deinstancing of the containing module. This is handled with the notify_inst/notify_deinst method attributes rather than with parameters attributes (see Setting Method Attributes on page 8-6 for details).
For a discussion of how event notification works, see Event Notification of Methods on page 8-11.
Setting the req attribute on a parameter prevents the triggering of the associated method if that parameter does not have a valid value, regardless of whether the trigger event is an instancing or deinstancing or a change of parameter value. Leave this attribute unset only if you want your instance event to be called even if parameters are not valid.

By default, all of these parameter attributes are off. You can turn them on either on a parameter, on the method associated with the parameter, or on both parameters and methods.

Setting Parameter Attributes on Parameters

The following example defines a module named test, with parameters named p1 and p2 and a method named update:

module test {
   int+read+notify p1;
   int+read+notify p2;
   omethod update = "test_update";
};

Observe that in this example, the notify and read attributes are set on the p1 and p2 parameters. The update method reads p1 and p2 and is notified whenever the value of p1 or p2 changes.

Setting Parameter Attributes on Methods

When you set a parameter attribute on a method, you overwrite the default behavior for the associated parameters; that is, each parameter takes the setting specified at the method level. This is a convenient way to deal with multiple methods.

Like the first example in the previous section, the following example V code defines a module named test, with parameters named p1 and p2 and a method named update:

module test {
   int p1;
   int p2;
   omethod+notify+read update = "test_update";
};

In this case, the notify and read attributes are set on the update method. Again, the method reads p1 and p2 and is notified whenever the value of p1 or p2 changes. The result is the same, although the technique is different.

Another way to set parameter attributes on a per-method basis is using the parameter/attribute syntax illustrated in the following example:

module test {
   int p1;
   int p2;
   omethod update(p1+read+notify, p2+write)="test_update";
};

When you specify attributes in this way, the method is only affected only by the parameters included in the parentheses. Parameters not included do not affect the method even if they have the notify, read or write attributes set. Although this parameter syntax is similar to C function parameter declarations, using it does not affect the way in which the target function is called (test_update in this example). The C function's argument list is always the same (see C Target Functions on page 8-16 for details).

Setting Parameter Attributes on Parameters and Methods

You are not restricted to specifying parameter attributes either on a parameter or on the associated method. You can combine attribute settings on methods and parameters to achieve specific results. In the following example, the update method reads and is notified by p1 and p2, but writes and is not notified by p3:

module test {
   int p1;
   int p2;
   int+nonotify+noread+write p3;
   omethod+notify+read update = "test_update";
};
Setting Parameter Attributes on Hierarchical Parameters

A parameter for which read/noread, write/nowrite, or notify/nonotify is not set inherits the setting for its parent object. (If the parameter is an immediate child of a module, it uses the setting for the associated method, as described in Setting Parameter Attributes on Methods on page 8-8.) If the parameter is a hierarchical object, the attribute is in effect downstream from the parameter until it is explicitly turned on or off. The following example defines a module named test, with a number of subobjects:

module test {
   group+read p1 {
      int sub1_p1
      int+noread+nonotify sub2_p1;
   };
   int+read p2;
   omethod+notify update = "test_update";
};

In this example, the update method reads and is notified by the parameter p1.sub1_p1, but it does not read and is not notified by the parameter p1.sub2_p1.

For efficiency, the req attribute behaves differently than read/noread, write/nowrite,and notify/nonotify. Its setting is not propagated: if it is to be applied to a particular parameter, you must set it at each level in the object hierarchy starting with the method itself.

The following example sets the req attribute on the parameter p1 but not the parameter p2:

module test {
   int+req p1;
   int p2;
   omethod+req update = "test_update";
};

The setting of the req attribute is also not inherited for hierarchical parameters:

module test {
   group+req p1 {
      int sub1_p1
      int+req sub2_p1;
   };
   omethod+req update = "test_update";
};

In this example, the update method is not executed if p1.sub2_p1 does not have a valid value, but is executed even if p1.sub1_p1 does not have a valid value.

Defining the Scope of a Method

A module's method is influenced only by parameters that have the same parent as the method, in the scope where the method was originally defined. The following example defines a module named mod1, with parameters named x and y and a method named upd:

module mod1 {
int x, y;
omethod+notify+read upd = "func1";
};

In this example, the upd method, which has the same parent as the x and y parameters, reads and is notified by both. You now create a module called mod2 that inherits from mod1 and defines an additional parameter named z:

mod1 mod2 {
   x+nonotify;
int z;
};

In mod2, the inherited upd method is notified if x or y changes but, because it does not have the same parent as z, it is not notified if z changes.

This scope feature allows you to copy an existing module and add additional parameters and methods without interfering with the existing module's functionality. If you want to replace the functionality defined by an existing method, simply define a new method with the same name.

In some cases, however, the behavior resulting from the scope feature may not be desirable. For example, you might want the user of an object to be able to add additional parameters that cause the update method to run. To override the scope feature, set the property no_meth_ctx on the method to a value of 1.

Suppose, for example, that you redefine mod1 by adding the no_meth_ctx attribute to the upd method as follows:

module mod1 {
int x, y;
omethod+notify+read upd<no_meth_ctx=1> = "update";
};

You now create the module mod2 exactly as you did before:

mod1 mod2 {
x+nonotify;
int z;
};

Because of the no_meth_ctx attribute, the upd method is no longer notified by x but is notified by z.

Event Notification of Methods

Previous sections described how to enable notifying a method when a desired trigger event occurs, typically the instancing or deinstancing of a module or a change to a parameter value. This section discusses how AVS/Express event notification occurs.

When a trigger event occurs, AVS/Express queues an event to execute one or more methods at the end of the current operation. By default, each method executes inside of a single operation; that is, no other methods execute until your current method returns. If multiple events are queued to execute the same method, they are compressed into a single method invocation.

Each operation is controlled using a context. At the start of the operation, a context is pushed onto a stack with the push_ctx function. At the end of the operation, the context is popped off the stack with the pop_ctx function restoring the previous context (if any). Contexts can be nested inside of each other but cannot overlap.

Contexts
A context contains an event filter, a state mode, and an event mode.
For example, when you select a filename from a file browser widget, the widget calls push_ctx to set the state mode to user state, then changes the value of its filename parameter. This value is then marked as user state.
You can override the state mode on an object using the val_state property. For example, if you want the filename parameter of the user interface widget to be marked as program state, you can set the val_state property on this object to be 1:
UIfileDialog {
filename<val_state=1>;
};
Note: You must set the val_state property on the object that actually stores the value of the object. In this case, if the filename parameter is connected to another value, you must set the val_state property on the object at the end of the connection.
push_ctx(A) push_ctx(B) pop_ctx(B) pop_ctx(A)
If you specify this event mode and there is no parent context, AVS/Express executes the events in the current context. When the system executes a method, it calls push_ctx before it calls the method with this argument.

The push_ctx and pop_ctx routines are available to you in the C API and the C++ API, but not the FORTRAN API. For their descriptions, see the AVS/Express online help.

Controlling Method Execution Order

When a single operation triggers the execution of multiple methods, method dependencies determine the execution order: a method that depends upon another is executed after it. For example, if Method A reads data that method B writes, Method A depends upon method B and executes after it (which prevents Method A from executing twice).

Here is a more detailed example. Suppose that you have defined two modules, modB and modA, and the int common as follows:

int common;

module modB {
int+read+notify p1 => common;
int+write p2;
omethod update = "modB_update";
};
module modA {
int+read+notify p1 => common;
int+read+notify p2 => modB.p2;
omethod update = "modA_update";
};

The p2 parameter of modA, which modA reads and is notified by, reads the p2 parameter of modB. As a result, the method modA.update depends upon modB.update. If the integer common changes, it queues events for both modB and modA. However, because modA depends upon the output of modB, it runs after modB.

If you are unsure of a method's dependencies, call the V command $deps to determine them; for example:

OM(SingleWindowApp)->         $deps modA.update
function: Root.Applications.SingleWindowApp.modA.update depends on:
--------------------------------------------
   func: Root.Applications.SingleWindowApp.modB.update
   read/write on: Root.Applications.SingleWindowApp.modB.p2
   weight: 1, rc: 1, req=0
===============================================

For a reference description of the V command $deps, see the AVS/Express on-line help.

8.3 Target Functions

When you include a method in a module, you must provide a C, C++, or FORTRAN target function that the method executes when it is triggered. A target function contains a mix of native code and calls to Object Manager API routines. These API routines, which enable communications with the Object Manager, perform tasks such as getting and setting the values of AVS/Express objects, navigating the object hierarchy, creating and deleting objects, and controlling execution.

Consider the following when you select a language for your target function:

Writing Target Functions

You can write a target function for your method from scratch, but it is far simpler to let AVS/Express do most of the work for you. In summary, all you must do is:

1. Define the module.
2. Set it up for the type of target function that you want to use (C, C++, or FORTRAN), as described in subsequent sections.
3. Compile the process that contains the module:
a. Select the module in the Network Editor.
b. Selecting Project->Compile from the Network Editor menu bar.
AVS/Express generates a template source fileand places it in the following file:
my_proj_dir/my_build_dir/mod_name.lang
4. Edit the template source file as required (see the next section).

Some details of this process are slightly different for C++ target functions (see Supplying C++ Source Code on page 8-19 for details).

The following sections provide specifics for setting up modules so that their methods can execute C, C++, and FORTRAN target functions. It does not provide discussions of how to use Object Manager API routines within a target function; for details, see Chapter 9, Object Manager APIs.

Editing Source Code

AVS/Express provides a special command for editing source files. Note that it works only with source files specified with the src_file property, and not with source files specified with the c_src_files property. To use this command:

1. Set the EDITOR environment variable to specify an editor of your choice.
2. In the Network Editor, select the object whose associated source file you want to edit.
3. Select the Project->Edit Source pull-down command.

AVS/Express starts the specified editor for the selected object's source file and for all source files below the object in the object hierarchy. Using this command is equivalent to issuing a command line that starts the editor for all of those source files.

C Target Functions

The example module ave, defined in V code as follows, averages two numbers:

module ave {
float+read+notify num1;
float+read+notify num2;
float+write res;
omethod+notify_inst upd = "ave_upd";
};

When the appropriate event occurs, this module performs its processing by triggering the upd method, which executes the C target function ave_upd. This function is defined in the file ave.c as follows:

#include <avs/om.h>
#include <avs/err.h>

/*
 * ave_upd gets the module id, event_mask and.seq_num 
 */
int ave_upd(OMobj_id obj_id,
  OMevent_mask event_mask, /* triggering events */
  int seq_num) /* sequence number of last exec */
{
double num1, num2, res;
if (OMget_name_real_val(obj_id,
    OMstr_to_name("num1"),
    &num1) != 1)
return(0); /* module failed... input not valid */

if (OMget_name_real_val(obj_id,
    OMstr_to_name("num2"),
    &num2) != 1)
return(0);

res = (num1 + num2) / 2;
OMset_name_real_val(obj_id,
OMstr_to_name("res"),
res);

return(1);
}

For details on the C API routines used in this function, see C and C++ API Routines on page 9-5.

Specifying the cmethod Type

In the V code definition of the module ave, note that the method's type is omethod. Recall that you define a method as an omethod type to indicate that it executes a C target function.

Specifying Code Management Properties

Once you define a module, you must specify code management properties that tell AVS/Express what code it must generate to define your target function. You can specify these properties to point directly to the source or you can point the system at a set of object files or archived libraries (see Providing Code Management Information on page 8-28).

Specifying An Executable

Before you can compile a module, you must ensure that it will be compiled into the correct process (that is, executable) and correct the specification as necessary. You specify a process with the process property, which you can set either directly on the module or on the library in which you place it (see Defining Processes for Objects on page 10-12).

Suppose that you want to copy the module ave into the library Templates.USER, whose process property is set to compile the module into the user process and whose build directory is set to the user directory. You must revise the module definition as follows to specify the source file ave.c, which contains your C function, and your own build directory:

module ave<src_file="ave.c", build_dir="my_build_dir"> {
float+read+notify num1;
float+read+notify num2;
float+write res;
omethod+notify_inst upd = "ave_upd";
};

You can now compile the process containing the module ave.

Target Function Execution

When a target function executes (that is, when its method is triggered and calls it), the function receives three arguments: the object ID of its module, the event mask (which indicates the events that triggered the method), and a sequence number.

Through calls to the C API, the target function gets and sets the values of the module's subobjects. If any inputs are not set, the function returns a 0 value, to indicate that the module has failed. If the module fails, any modules with required dependencies on its outputs do not run until it executes successfully. If the module succeeds, it returns 1.

C++ Target Functions

All of the basic rules for developing modules (see Module Overview on page 8-4) apply to modules written to use C++ target functions, with a few minor differences as documented in this section.

Specifying the cxxmethod Type

If a method for your module executes a C++ function, you specify its type as cxxmethod. You assign the notify_inst, notify, req, and notify_deinst properties to a cxxmethod object just as you do for other method types; however, supplying a string value for object is optional and, if present, the string value is handled differently than for other method types.

For details, see Supplying C++ Source Code on page 8-19.

C++ Methods for the cxxmethod Type

When a module has a method of type cxxmethod, AVS/Express automatically generates a C++ class for it. Each cxxmethod object defines a C++ method that belongs to this class and whose name is the same as the name of the cxxmethod object. Each C++ method takes event_mask and seq_num parameters:

For more information on using this parameter, see Determining When Parameters Change on page 9-57.

Suppose, for example, that you define the module mod1 as follows:

module mod1 {
cxxmethod upd;
};

For this module, you must implement a C++ method like the following:

int
mod1::upd(OMevent_mask event_mask, int seq_num)
{
// module code goes here...
return(1);
}

This routine returns either 1 (success) or 0 (failure). If it fails, AVS/Express prohibits any modules with required dependencies on the outputs of this method from running until it executes successfully.

Supplying C++ Source Code

When you define a cxxmethod object, you can supply the source code for the corresponding C++ method in any of the following ways:

Using the AVS/Express Prototype Generation Facility

When AVS/Express compiles a process containing a module that includes a cxxmethod object, it can generate a prototype for the corresponding C++ method. You enable this prototype generation facility by setting the src_file property either on the module's cxxmethod object, the module, or the module's containing library, and leaving the value of the cxxmethod object unset. At compilation time, AVS/Express starts looking for the src_file property at the level of the cxxmethod object, ascending the object hierarchy if necessary, and chooses the first src_file property that it encounters. The specified file is used as the holder of the source code for the cxxmethod object. (If AVS/Express does not find a src_file property, it prints a warning message.)

When AVS/Express finds a src_file property, it scans the specified file for a method named module_name::method_name (for example, mod1::update). If such a method exists, AVS/Express does not generate code; however, if such a method does not exist, AVS/Express produces a prototype C++ method and places it at the end of the file. The prototype contains code only for those parameters that the cxxmethod object reads and/or writes; if the cxxmethod object sets neither the read nor the write flag on a parameter, AVS/Express does not generate code for it. The generated code varies depending on the parameter types, as follows:

You can edit the generated source code as necessary using the Network Editor's Edit Source facility.

Suppose that you define a module named test as follows:

module test<src_file="test.cxx"> {
Mesh_Unif+Scalar+notify+read &input;
int+write output;
cxxmethod update;
};

When you compile the process that contains this module, AVS/Express generates the following prototype code in the source file test.cxx:

   int
   test::update(OMevent_mask event_mask, int seq_num)
   {
   // input (Mesh_Unif+group read)
   // input.ndim (int)
   // input.dims (int [])
   int *input_dims = (int *)input.dims.ret_array_ptr(OM_GET_ARRAY_RD);
   // input.nspace (int)
   // input.npoints (int)
   // input.points (float [])
   float *input_points = (float *)input.points.ret_array_ptr(OM_GET_ARRAY_RD);
   
   // output (OMXint write)
   

   /***********************/
   /*    Function Body    */
   /***********************/
   printf("I'm in method: test::update\n");
   
   ARRfree((char *)input_dims);
   ARRfree((char *)input_points);
   
   // return 1 for success
   return(1);
   }

Once the prototype code for a C++ method has been generated, the only way to have AVS/Express regenerate it is to rename or remove the method module_name::method_name. AVS/Express does not attempt to keep the prototype code up to date, which allows you complete freedom to modify the prototype source code.

Specifying Source Code in the Module Definition

Unlike module definitions s that use C or FORTRAN target functions, AVS/Express allows you to define a module with a C++ method entirely in V code, bypassing the need to create and maintain subsidiary files. To do this. you embed the source code in the module definition itself by supplying as the value of the cxxmethod object a string value that contains the body of the C++ method. At compilation time, AVS/Express places the code in the current output source file. By default, AVS/Express creates a default output source file named process.cxx, but you can specify a file explicitly with the out_src_file property. This embedded-source technique is useful for very small routines with limited external dependencies, but it has one disadvantage: any user who can edit the module definition also has access to its source code.

The string value that specifies your C++ source code may need to be long, so AVS/Express allows you to specify unescaped strings using V syntax rather than the standard double-quote string syntax. In V code, a string begins with the characters <" and ends with the characters ">. Note that your source code must include, as well as the primary code, a return statement (usually return(1), to indicate success).

The following example completely defines the module by_itself:

module by_itself {
int+read+notify a;
int+write b;
cxxmethod+notify_inst update = <"
if (a > b) b = a;
return(1);
">;
};

You can also use the embedded-source technique to define a module that calls a subroutine defined elsewhere. In the following example, the source code for the cxxmethod object references a user-written function named user_subroutine:

module subroutine_call {
int+read+notify ip1, ip2, ip3;
int+write ret_val;
cxxmethod+notify_inst update = <"
ret_val = user_subroutine(ip1, ip2, ip3);
if (ret_val > 0) return(1);
return(0);
">;
};

Because C++ requires that every function be defined before it is used, you must use the cxx_hdr_files property to tell AVS/Express which header files include this definition.

For details, see Managing Dependencies on Other Code on page 9-60.

Detaching the cxxmethod Object from the AVS/Express Prototype Generation Facility

If you do not want to use the src_file property mechanism to specify source code for your module's C++ method, you must set the use_src_file property to 0 on the module's cxxmethod object, the module, or the module's containing library. However, you must still tell AVS/Express how to get the source code. You can do this in either of the following ways:

Example Module with cxxmethod Object

This section presents an example AVS/Express module that contains a cxxmethod object. (For details of how data is manipulated with this example, see C++ API Usage Notes on page 9-48.) The module, named EXquad_solver, implements a simple quadratic equation solver that takes three parameters, a, b, and c, which are the coefficients of a simple quadratic polynomial. If there are any defined roots for the specified coefficients, the module places them into the parameters r1 and r2 and returns the status message "Roots are real". If there are no defined roots, it sets r1 and r2 to 0 and returns the status message "Roots are imaginary." The module's method is called update_roots.

The EXquad_solver module is defined in V code as follows:

module EXquad_solver<
src_file="quad_mod.cxx",  // specify file containing module src
out_hdr_file="quad_gen.h" // specify file to place
  generated class
> {
float+read+notify+req a, b, c;
float+write r1, r2;
string+write status;
cxxmethod+notify_inst+req update_roots;
};

In this definition, note the following:

When the appropriate event occurs and the update_roots method is triggered, it executes the following C++ method, defined in the file quad_mod.cxx:

#include "quad_gen.h" // specified as out_hdr_file
// on EXquad_solver object

int
EXquad_solver::update_roots(OMevent_mask event_mask, int seq_num)
{
float op = b * b - 4 * a * c;
if (op < 0) {
status = "Roots are imaginary";
r1 = r2 = 0;
}
else {
op = sqrt(op);
float div = 2 * a;
if (div != 0.0) {
status = "Roots are real";
r1 = (-b + op) / div;
r2 = (-b - op) / div;
}
else {
status = "Roots are infinite";
r1 = 0;
r2 = 0;
}
}
return(1);
}

The variables a, b, c, r1, r2, and status are part of the C++ object that is an implicit argument to this C++ method. You can also reference these values as follows:

float op = this->b * this->b - 4 * this->a * this->c;
FORTRAN Target Functions

The example module ave, defined in V code as follows, averages two numbers:

module ave {
float+read+notify num1;
float+read+notify num2;
float+write res;
fmethod+notify_inst upd = "ave_upd";
};

When the appropriate event occurs, this module performs its processing by triggering the upd method, which executes the FORTRAN target function ave_upd. This function is defined in the file ave.f as follows:

C
C  Source for fortran function ave_upd
C
      integer function ave_upd(obj_id, mask, seq_num)
      include 'avs/omf.inc'
      integer obj_id(OIDSIZ)
      integer mask
      integer seq_num
      integer name
      integer num1_id(OIDSIZ), num2_id(OIDSIZ), res_id(OIDSIZ)
      double precision num1, num2, res
      ave_upd = 0
C
C  Get the ids for each of our 3 arguments, num1, num2 and res
C  Since we only access the first two values, we can use the flag
C  OM_OBJ_RD. Since we modify the 3rd one we have to use OM_OBJ_RW.
C
      name = OMFstr_to_name('num1')
      if (OMFfind_subobj(obj_id,name,OM_OBJ_RD,num1_id) .NE. 1)
     & return
      name = OMFstr_to_name('num2')
      if (OMFfind_subobj(obj_id,name,OM_OBJ_RD,num2_id) .NE. 1)
     & return
      name = OMFstr_to_name('res')
      if (OMFfind_subobj(obj_id,name,OM_OBJ_RW,res_id) .NE. 1)
     & return
C
C  Get the double precision floating point values for num1 and num2
C  Express always returns values in double precision format
C
      if (OMFget_real8_val(num1_id,num1) .NE. 1) return
      if (OMFget_real8_val(num2_id,num2) .NE. 1) return
      res = (num1 + num2) / 2
      if (OMFset_real8_val(res_id, res) .NE. 1) return
C
C  Set return status to success
C
      ave_upd = 1
      return
      end

For details on the various FORTRAN API routines used in this function, see FORTRAN API Routines on page 9-73.

Specifying the fmethod Type

In the V code definition of the module ave, note that the method's type is fmethod. Recall that you define a method as an fmethod type to indicate that it executes a FORTRAN target  function.

Notice that upd's type is fmethod. This tells AVS/Express that you intend to use the FORTRAN API.

Notice that upd's type is fmethod. This tells AVS/Express that you intend to use the FORTRAN API.

Specifying Code Management Properties

Once you define a module, you must specify code management properties that tell AVS/Express what code it must generate to define your target function. You can specify these properties to point directly to the source or you can point the system at a set of object files or archived libraries (see Providing Code Management Information on page 8-28).

Specifying An Executable

Before you can compile a module, you must ensure that it will be compiled into the correct process (that is, executable) and correct the specification as necessary. You specify a process with the process property, which you can set either directly on the module or on the library in which you place it (see Defining Processes for Objects on page 10-12).

Suppose that you want to copy the module ave into the library Templates.USER, whose process property is set to compile the module into the user process and whose build directory is set to the user directory. You must revise the module definition as follows to specify the source file ave.c, which contains your C function, and your own build directory:

module ave<src_file="ave.f", build_dir="my_build_dir"> {
float+read+notify num1;
float+read+notify num2;
float+write res;
fmethod+notify_inst upd ="ave_upd";
};

You can now compile the process containing the module ave.

Target Function Execution

When a target function executes (that is, when its method is triggered and calls it), the function receives three arguments: the object ID of its module, the event mask (which indicates the events that triggered the method), and a sequence number.

Through calls to the FORTRAN API, the target function gets and sets the values of the module's subobjects. If any inputs are not set, the function returns a 0 value, to indicate that the module has failed. If the module fails, any modules with required dependencies on its outputs do not run until it executes successfully. If the module succeeds, it returns 1.

8.4 Providing Code Management Information

When you define a module (or library of modules), you must provide AVS/Express with information about the code upon which each group of modules depends. This includes, but is not limited to, information such as make commands, a build directory name, header files to be included, and source files to compile.

You provide this information by adding special code management properties to objects in the template hierarchy. You can define either a close relationship or an indirect relationship with this system:

You typically place code management properties on modules or libraries, but you can place them directly on methods. When you place a code management property on a library, all objects in the library inherit it. When you place it on a module or method, only the module or method inherits it.

Once you assign code management properties, AVS/Express uses them to provide the following functionality:

For a list and descriptions of all code management properties, see Code Management Properties on page 14-13.


TOC PREV NEXT INDEX