Using the C Math Library    

Example - Passing Functions As Arguments (ex4.c)

To illustrate function-functions, this example program uses the ordinary differential equation (ODE) solver mlfOde23() to compute the trajectory of the Lorenz equation. Given a function, F, and a set of initial conditions expressing an ODE, mlfOde23() integrates the system of differential equations, y' = F(t,y), over a given time interval. mlfOde23() integrates a system of ordinary differential equations using second and third order Runge-Kutta formulas. In this example, the name of the function being integrated is lorenz.

For convenience, this example has been divided into three sections; in a working program, all of the sections would be placed in a single file. The first code section specifies header files, declares global variables including the local function table, and defines the lorenz function.

The numbered items in the list below correspond to the numbered comments in the code example:

  1. Include "matlab.h". This file contains the declaration of the mxArray data structure and the prototypes for all the functions in the library. stdlib.h contains the definition of EXIT_SUCCESS.
  2. Declare SIGMA, RHO, and BETA, which are the parameters for the Lorenz equations. The main program sets their values, and the lorenz function uses them.
  3. Declare a static global variable, MFuncTab[], of type mlfFuncTabEnt. This variable stores a function table entry that identifies the function that mlfOde23() calls. A table entry contains three parts: a string that names the function ("lorenz"), a pointer to the function itself ((mlfFuncp)lorenz), and a pointer to the thunk function that actually calls lorenz, (_lorenz_thunk_fcn_). The table is terminated with a
    {0, 0, 0} entry.

    Before you call mlfOde23() in the main program, pass MFuncTab to the function mlfFevalTableSetup(), which adds your entry to the built-in function table maintained by the MATLAB C Math Library. Note that a table can contain more than one entry.

  1. Define the Lorenz equations. The input is a 1-by-1 array, tm, containing the value of t, and a 3-by-1 array, ym, containing the values of y. The result is a new 3-by-1 array, ypm, containing the values of the three derivatives of the equation at time = t.
  2. Create a 3-by-1 array for the return value from the lorenz function.
  3. Calculate the values of the Lorenz equations at the current time step. (lorenz doesn't use the input time step, tm, which is provided by mlfOde23.) Store the values directly in the real part of the array that lorenz returns. yp points to the real part of ypm, the return value.

The next section of this example defines the thunk function that actually calls lorenz. You must write a thunk function whenever you want to pass a function that you've defined to one of MATLAB's function-functions.

The numbered items in the list below correspond to the numbered comments in the code example:

  1. Define the thunk function that calls the lorenz function. A thunk function acts as a translator between your function's interface and the interface needed by the MATLAB C Math Library.

    The thunk function takes five arguments that describe any function with two inputs and one output (in this example the function is always lorenz()): an mlfFuncp pointer that points to lorenz(), an integer (nlhs) that indicates the number of output arguments required by lorenz(), an array of mxArray's (lhs) that stores the results from lorenz(), an integer (nrhs) that indicates the number of input arguments required by lorenz(), and an array of mxArray's (rhs) that stores the input values. The lhs (left-hand side) and rhs (right-hand side) notation refers to the output arguments that appear on the left-hand side of a MATLAB function call and the input arguments that appear on the right-hand side.

  1. Define the type for the lorenz function pointer. The pointer to lorenz comes into the thunk function with the type mlfFuncp, a generalized type that applies to any function.

    mlfFuncp is defined as follows:

    typedef void (*mlfFuncp)(void)

    The function pointer type that you define here must precisely specify the return type and argument types required by lorenz. The program casts pFunc to the type you specify here.

    The name PFCN_1_2 makes it easy to identify that the function has 1 output argument (the return) and 2 input arguments. Use a similar naming scheme when you write other thunk functions that require different numbers of arguments. For example, use PFCN_2_3 to identify a function that has two output arguments and three input arguments.

  1. Verify that the expected number of input and output arguments have been passed. lorenz expects two input arguments and one output argument. (The return value counts as one output argument.) Exit the thunk function if too many input or output arguments have been provided. Note that the thunk function relies on the called function to do more precise checking of arguments.
  2. Call lorenz, casting pFunc, which points to the lorenz function, to the type PFCN_1_2. Verify that the two expected arguments are provided. If at least one argument is passed, pass the first element from the array of input values (rhs[0]) as the first input argument; otherwise pass NULL. If at least two arguments are provided, pass the second element from the array of input values (rhs[1]) as the second argument; otherwise pass NULL as the second argument. The return from lorenz is stored temporarily in the local variable Out.

    This general calling sequence handles optional arguments. It is technically unnecessary in this example because lorenz has no optional arguments. However, it is an essential part of a general purpose thunk function.

    Note that you must cast the pointer to lorenz to the function pointer type that you defined within the thunk function.

  1. Assign the value returned by lorenz to the appropriate position in the array of output values. The return value is always stored at the first position, lhs[0]. If there were additional output arguments, values would be returned in lhs[1], lhs[2], and so on.
  2. Return success.

The next section of this example contains the main program. Keep in mind that in a working program, all parts appear in the same file.

The numbered items in the list below correspond to the numbered comments in the code example:

  1. Declare and initialize variables. tspan stores the start and end times. y0 is the initial value for the lorenz iteration and contains the vector 10.0, 10.0, 10.0.
  2. Add your function table entry to the MATLAB C Math Library built-in feval function table by calling mlfFevalTableSetup(). The argument, MFuncTab, associates the string "lorenz" with a pointer to the lorenz function and a pointer to the lorenz thunk function. When mlfOde23() calls mlfFeval(), mlfFeval() accesses the library's built-in function table to locate the function pointers that are associated with a given function name, in this example, the string "lorenz".
  3. Assign values to the equation parameters: SIGMA, RHO, and BETA. These parameters are shared between the main program and the lorenz function. The lorenz function uses the parameters in its computation of the values of the Lorenz equations.
  4. Create two arrays, tsm and ysm, which are passed as input arguments to the mlfOde23 function. Initialize tsm to the values stored in tspan. Initialize ysm to the values stored in y0.
  5. Call the library routine mlfOde23(). The return value and the first argument store results. mlfOde23() is a varargout function; mlfVarargout(NULL) indicates that you are not interested in supplying any varargout arguments. Pass the name of the function, two required input arguments, and NULL values for the two optional input arguments.

    mlfOde23() calls mlfFeval() to evaluate the lorenz function. mlfFeval() searches the function table for a given function name. When it finds a match, it composes a call to the thunk function that it finds in the table, passing the thunk function the pointer to the function to be executed, also found in the table. In addition, mlfFeval() passes the thunk function arrays of input and output arguments. The thunk function actually executes the target function.

  1. Prepare results for printing. The output consists of four columns. The first column is the time step and the other columns are the value of the function at that time step. The values are returned in one long column vector. If there are n time steps, the values in column 1 occupy positions 0 through n-1 in the result, the values in column 2, positions n through 2n-1, and so on.
  2. Print one line for each time step. The number of time steps is determined by the number of rows in the array tm returned from mlfOde23. The function mxGetM returned the number of rows in its mxArray argument.
  3. Free all bound arrays and exit.

Output

The output from this program is several pages long. Here are the last lines of the output.


 Extending the mlfFeval() Table Replacing Argument Lists with a Cell Array