Next: Function Handles and Inline, Previous: Script Files, Up: Functions and Scripts
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.
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.
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.