Using the C++ Math Library    

Example Program: Passing Functions As Arguments (ex3.cpp)

This example covers advanced material. You only need to read this section if you're using a MATLAB C++ Math Library function that requires another function as an argument.

You can find the code for this example in the
<matlab>/extern/examples/cppmath directory on UNIX systems and in the <matlab>\extern\examples\cppmath directory on PCs, where <matlab> represents the top-level directory of your installation. See Building C++ Applications for information about building and running the example program.

Certain functions in the MATLAB C++ Math Library, for example, fmins() and fzero(), require user-supplied functions as arguments. fmins() and the functions like it are called "function-functions," because they operate on functions rather than arrays. This example demonstrates how to write a function that a function-function can call.

The library supports two methods of registering your function with the MATLAB C++ Math Library: the first, and easiest, uses the feval macros; the second requires that you write a thunk function and define and populate a local table that identifies your function for the library. The macro method performs these tasks for you.

Both methods are presented in this example. The macros support registration of the most common types of functions. You only need to use the manual, nonmacro method in certain special cases (detailed below). Read the step-by-step, nonmacro version if you want to understand in detail how the MATLAB C Math Library function mlfFeval() executes the functions passed to it.

The MATLAB C Math Library forms the foundation for the MATLAB C++ Math Library. For the most part, the MATLAB C Math Library provides its services transparently, but there are a few places where its interface is visible.

To execute a function passed to a function-function, the C++ Math Library calls the C Math Library function mlfFeval(). mlfFeval() calls a thunk function that actually executes the function passed to it. That thunk function must have a C interface along with the table that identifies your function to the library. Refer to the example "Passing Functions as Arguments" in the MATLAB C Math Library User's Guide for more information on how mlfFeval() and thunk functions work.

In this example, note the following:

Using the feval Macros

Use the feval macros to register a function that you've written for execution by a function-function or feval(). The macros register any function that takes a combination of 0 to 8 input arguments and 1 to 5 output arguments. If you need to register a function that takes more than 8 inputs or more than 5 outputs, you cannot use the feval macros; you must write your own thunk function and manually construct an feval function table.

The functions func1() and main() are the same in both versions of this example. The feval macros replace the thunk function, a typedef, a mlfFuncTabEnt declaration, and the feval_init class that you'll find in the nonmacro version.

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

  1. Include header files.  matlab.hpp declares the MATLAB C++ Math Library's data types and functions.  matlab.hpp includes iostream.h, which declares the input and output streams cin and cout.  stdlib.h contains the definition of EXIT_SUCCESS.
  2. Declare func1(). The name of this function is subsequently passed to fmins(). During the execution of fmins(), control passes into the MATLAB C Math Library, which calls func1().

    func1() computes the natural logarithms and the square roots of the elements in the input matrix and multiplies them together. The two functions, reallog() and realsqrt() guarantee that their outputs are noncomplex matrices, i.e., matrices that have only real (no imaginary component) elements. Note that this computation means nothing mathematically.

  1. Use the feval macros to register func1() as a function that can be executed by a function-function or feval(). Begin the table with the DECLARE_FEVAL_TABLE macro, and end the table with the END_FEVAL_TABLE macro. Pass the func1 function pointer to the FEVAL_ENTRY macro. The macros perform all the tasks required.

    The full form of the macro is:

    The macros are placed outside of all function definitions and appear after a declaration of the functions being registered.

    For example,

    However, you do not have to register a function in the same file as the call to the function-function or feval(). Only one set of macros can appear in any given source file, though you can register additional functions by using the macros in another source file.

  1. Call fmins() from the main program, passing the string "func1" as the first argument, and print the result. fmins() computes a local minimizer of func1() near its second argument, the scalar 0.25.

feval( ) Without the Macros

The example is divided into three parts. The first part defines the function func1() and shows the main program. The second part specifies the local feval function table. The third part defines the thunk function. In the C++ source file, the parts would be combined in this order: func1(), the thunk function, the feval table code, and main().

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

  1. Include header files. matlab.hpp declares the MATLAB C++ Math Library's data types and functions. matlab.hpp includes iostream.h, which declares the input and output streams cin and cout.  stdlib.h contains the definition of EXIT_SUCCESS.
  2. Declare func1(). The name of this function is later passed to fmins(). During the execution of fmins(), control passes into the MATLAB C Math Library, which calls func1().
  3. Compute the natural logarithms and the square roots of the elements in the input matrix and multiply them together. The two functions, reallog() and realsqrt(), guarantee that their outputs are noncomplex matrices, i.e., matrices that have only real (no imaginary component) elements. Note that this computation means nothing mathematically.
  4. Call fmins() from the main program, passing the string "func1" as the first argument, and print the result. fmins() computes a local minimizer of func1() near its second argument, the scalar 0.25.

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

  1. Declare a static global variable, MFuncTab[] of type mlfFuncTabEnt. This local function table stores one or more function table entries that identify any functions (that you've written) to be executed by a MATLAB C Math Library function-function. In this example, the table stores one entry that identifies the function that fmins() executes, func1().
  2. Add an entry to the function table. The entry is composed of three parts: a string, "func1", that names the function, a pointer, (mlfFuncp)func1, to the function itself, and a pointer, one_input_one_output, to the thunk function that actually calls func1().

    Notice the mlf prefix in the names of the mlfFuncTabEnt and mlfFuncp types. These are types used by the MATLAB C Math Library and are used to tell the C Math Library function mlfFeval() about your function. For more information on the mlfFuncTabEnt and mlfFuncp types, see the file matlab.h in the include directory of your MATLAB installation.

  1. Terminate the table with a {0, 0, 0} entry.
  2. Define a private C++ class called feval_init that will initialize the local function table.
  3. Define a constructor feval_init(). In the body of the constructor, pass your function table, MFuncTab, to the C function mlfFevalTableSetup(). Here is another place where the C Math Library interface is used within your C++ application.
  4. Declare a class variable named feval_setup of type feval_init. The class feval_init thus contains a static instance of itself. When you define static member data for a class, you must subsequently declare that variable in your code.
  5. Now define the variable named feval_setup of type feval_init. The syntax feval_init::feval_setup specifies that the variable is contained within the class feval_init. This statement is executed when static variables are initialized. Because mlfFevalTableSetup() is called at this time by the constructor, you don't need to explicitly add your entries to the built-in function table maintained by the MATLAB C Math Library.

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

  1. Define the type for the functions handled by a thunk function. The function pointer type that you define here must precisely specify the return type and argument types required by func1().

    The typedef statement defines a function pointer type, PFCN_1_1, that takes one mwArray argument and returns an mwArray. The name PFCN_1_1 makes it easy to identify that the function has 1 output argument (the return) and 1 input argument. 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. Declare your thunk function as extern "C" to avoid C++ name translation.
  2. Declare your thunk function as static to avoid conflicts with other feval() calls from other files. Note that if your application requires you to write several thunk functions, and if several of your functions are associated with each thunk function, you may want to group the thunk functions in a separate file. In that case, do not declare the thunk functions static.
  3. Define the C-style thunk function that executes func1(). A thunk function is a translator between the interface required by the MATLAB C Math Library and your function's interface. You must use the MATLAB C Math Library's mlfFeval() calling convention for your thunk function because mlfFeval() calls your thunk function from within the C Math Library. Notice the arguments are of type mxArray rather than mwArray.

    The function takes five arguments that describe any one input, one output function (in this example the function is always func1()): an mlfFuncp pointer that points to func1(), an integer (nlhs) that indicates the number of output arguments required by func1(), an array of mxArray's (lhs) that stores the results from func1, an integer (nrhs) that indicates the number of input arguments required by func1(), and an array of mxArrays (rhs) that stores the input values. lhs stands for the left-hand side; rhs stands for the right-hand side.

  1. Verify that the expected number of input and output arguments have been passed. func1() expects one input argument and one output argument. (The return value counts as the one output argument.) Exit the thunk function if too many input or output arguments have been provided.
  2. The constructor that builds an mwArray object from an mxArray* has an optional second argument. If this argument is 1 (the default), the mwArray destructor frees the mxArray when its reference count reaches zero. If this argument is 0, as it is in this example, the mwArray destructor will never free the mxArray.

    This feature allows you to convert an mxArray to an mwArray temporarily without having the mwArray object free the mxArray when you don't want it to. Use this feature with caution, however, because it can lead to memory leaks; the program must free that mxArray eventually.

  1. Call func1(), casting pFunc, which points to func1(), to the type PFCN_1_1. Note that you must cast the pointer to func1() to the function pointer type that you defined.

    Verify that the expected input argument is provided. If at least one argument is passed to the thunk function, construct an mwArray from the first element in the array of input values (rhs[0]); pass that mwArray, not the mxArray, as the input argument to func1(). Otherwise, pass the special matrix, mwArray::DIN, that MATLAB C++ Math Library functions use to determine the number of inputs. The return from func1() is stored temporarily in the local variable Out, which is already a C++ mwArray.

    This line also demonstrates that you can call C++ routines from a C-style function like one_input_one_output(). Be very careful when calling C++ routines from a C routine. You must first manually convert the mxArray arguments into mwArray objects as demonstrated in Note 6. If you do not convert them manually, C++ will do so automatically, with unwanted consequences. The default mxArray to mwArray conversion routine assumes that the mxArray is freed when the last mwArray that references it goes out of scope. This is incorrect for matrices passed to C-style functions like one_input_one_output(). Failure to convert the matrices manually will lead to memory-related bugs that are often hard to track down.

  1. Extract the mxArray from the mwArray Out returned by func1(), and assign it to the appropriate position in the array of output values. The return value is always stored in the first position, lhs[0]. If there were additional output arguments, values would be returned in lhs[1], lhs[2], and so on.

    The thunk function calling convention requires that a C-style mxArray be returned rather than a C++-style mwArray object. It is necessary to modify the mwArray, Out, so that it does not free the mxArray it contains when the function terminates and Out goes out of scope.

    Use the mwArray member function FreezeData() to modify the mwArray. Use it very carefully. FreezeData() violates two of the principal design guidelines of the MATLAB C++ Math Library: it reaches into and modifies the array upon which it is invoked, and it provides a mechanism to circumvent the library's automatic memory management. Its effect is to release the mxArray* contained by the mwArray from automatic memory management.

    FreezeData() only works on mwArray objects that reference mxArray*s that have a reference count of one. See "The Space-Time Continuum" in Chapter 7 for more details on reference counting.

  1. Return success. A return value of 1 indicates success; 0 indicates failure.

Output

The program produces this output:


 How to Call Operators Representing Input Arguments As a Cell Array