Next: , Previous: Script Files, Up: Functions and Scripts


13.8 Dynamically Linked Functions

On some systems, Octave can dynamically load and execute functions written in C++. Octave can only directly call functions written in C++, but you can also load functions written in other languages by calling them from a simple wrapper function written in C++.

Here is an example of how to write a C++ function that Octave can load, with commentary. The source for this function is included in the source distributions of Octave, in the file examples/oregonator.cc. It defines the same set of differential equations that are used in the example problem of Ordinary Differential Equations. By running that example and this one, we can compare the execution times to see what sort of increase in speed you can expect by using dynamically linked functions.

The function defined in oregonator.cc contains just 8 statements, and is not much different than the code defined in the corresponding M-file (also distributed with Octave in the file examples/oregonator.m).

Here is the complete text of oregonator.cc:

just

     #include <octave/oct.h>
     
     DEFUN_DLD (oregonator, args, ,
       "The `oregonator'.")
     {
       ColumnVector dx (3);
     
       ColumnVector x (args(0).vector_value ());
     
       dx(0) = 77.27 * (x(1) - x(0)*x(1) + x(0)
                        - 8.375e-06*pow (x(0), 2));
     
       dx(1) = (x(2) - x(0)*x(1) - x(1)) / 77.27;
     
       dx(2) = 0.161*(x(0) - x(2));
     
       return octave_value (dx);
     }

The first line of the file,

     #include <octave/oct.h>

includes declarations for all of Octave's internal functions that you will need. If you need other functions from the standard C++ or C libraries, you can include the necessary headers here.

The next two lines

     DEFUN_DLD (oregonator, args, ,
       "The `oregonator'.")

declares the function. The macro DEFUN_DLD and the macros that it depends on are defined in the files defun-dld.h, defun.h, and defun-int.h (these files are included in the header file octave/oct.h).

Note that the third parameter to DEFUN_DLD (nargout) is not used, so it is omitted from the list of arguments in order to avoid the warning from gcc about an unused function parameter.

The next line,

     ColumnVector dx (3);

simply declares an object to store the right hand sides of the differential equation, and the statement

     ColumnVector x (args(0).vector_value ());

extracts a vector from the first input argument. The vector_value method is used so that the user of the function can pass either a row or column vector. The ColumnVector constructor is needed because the ODE class requires a column vector. The variable args is passed to functions defined with DEFUN_DLD as an octave_value_list object, which includes methods for getting the length of the list and extracting individual elements.

In this example, we don't check for errors, but that is not difficult. All of the Octave's built-in functions do some form of checking on their arguments, so you can check the source code for those functions for examples of various strategies for verifying that the correct number and types of arguments have been supplied.

The next statements

     dx(0) = 77.27 * (x(1) - x(0)*x(1) + x(0)
                      - 8.375e-06*pow (x(0), 2));
     
     dx(1) = (x(2) - x(0)*x(1) - x(1)) / 77.27;
     
     dx(2) = 0.161*(x(0) - x(2));

define the right-hand side of the differential equation. Finally, we can return dx:

     return octave_value (dx);

The actual return type is octave_value_list, but it is only necessary to convert the return type to an octave_value because there is a default constructor that can automatically create an object of that type from an octave_value object, so we can just use that instead.

To use this file, your version of Octave must support dynamic linking. To find out if it does, type the command octave_config_info ("dld") at the Octave prompt. Support for dynamic linking is included if this command returns 1.

To compile the example file, type the command `mkoctfile oregonator.cc' at the shell prompt. The script mkoctfile should have been installed along with Octave. Running it will create a file called oregonator.oct that can be loaded by Octave. To test the oregonator.oct file, start Octave and type the command

     oregonator ([1, 2, 3], 0)

at the Octave prompt. Octave should respond by printing

     ans =
     
        77.269353
        -0.012942
        -0.322000

You can now use the oregonator.oct file just as you would the oregonator.m file to solve the set of differential equations.

On a 133 MHz Pentium running Linux, Octave can solve the problem shown in Ordinary Differential Equations, in about 1.4 seconds using the dynamically linked function, compared to about 19 seconds using the M-file. Similar decreases in execution time can be expected for other functions, particularly those that rely on functions like lsode that require user-supplied functions.

Just as for M-files, Octave will automatically reload a dynamically linked function when the file that defines it is more recent than the last time that the function was loaded. If more than one function is defined in a single .oct file, reloading the file may force other functions to be cleared and reloaded. If all the functions loaded from a given .oct file are cleared, Octave will automatically unload the .oct file.

— Built-in Variable: warn_reload_forces_clear

If several functions have been loaded from the same file, Octave must clear all the functions before any one of them can be reloaded. If warn_reload_forces_clear, Octave will warn you when this happens, and print a list of the additional functions that it is forced to clear.

— Built-in Variable: variables_can_hide_functions

If the value of this variable is nonzero, assignments to variables may hide previously defined functions of the same name. A negative value will cause Octave to print a warning, but allow the operation.

Additional examples for writing dynamically linked functions are available in the files in the src directory of the Octave distribution. Currently, this includes the files

     balance.cc   fft2.cc      inv.cc       qzval.cc
     chol.cc      filter.cc    log.cc       schur.cc
     colloc.cc    find.cc      lsode.cc     sort.cc
     dassl.cc     fsolve.cc    lu.cc        svd.cc
     det.cc       givens.cc    minmax.cc    syl.cc
     eig.cc       hess.cc      pinv.cc
     expm.cc      ifft.cc      qr.cc
     fft.cc       ifft2.cc     quad.cc

These files use the macro DEFUN_DLD_BUILTIN instead of DEFUN_DLD. The difference between these two macros is just that DEFUN_DLD_BUILTIN can define a built-in function that is not dynamically loaded if the operating system does not support dynamic linking. To define your own dynamically linked functions you should use DEFUN_DLD.

There is currently no detailed description of all the functions that you can call in a built-in function. For the time being, you will have to read the source code for Octave.