Octave Wiki | RecentChanges

OctaveFortran

ScaryOctave > CategoryCode > CategoryExternal
This is a guide to incorporating Fortran code in Octave, by example. Octave is written in C++, so the approach taken here is to write C++ functions which call Fortran functions or subroutines, and then set up the C++ functions as `dynamically linked' functions for Octave. These can then be called from the Octave command line or in Octave functions just like any other Octave function.

First, a very simple example to get started. It won't be "hello, world", because strings are a bit tricky. Instead, say we have the Fortran subroutine

       SUBROUTINE NINE (R)
       DOUBLE PRECISION R
       R = 9D0
       END SUBROUTINE NINE

we'd write the C++ wrapper

 #include <octave/oct.h>
 #include "f77-fcn.h"
 extern "C"
 { 
   int F77_FUNC (nine, NINE) (double& R);
 }
 DEFUN_DLD (o9, args, ,
 	   "- Loadable Function: [NINE] = o9 ()\n\
      \n\
      Returns the number nine.")
 {
   octave_value_list retval;
   double r;
   F77_XFCN (nine, NINE, (r));
   if (f77_exception_encountered)
     {
       error ("unrecoverable error in o9");
       return retval;
     }
   retval(0) = octave_value (r);
   return retval;
 }

compile them (at the shell, not in Octave, the "$" being the shell's prompt) with

 $ mkoctfile o9.cc nine.f

assuming those are the names of the files containing the C++ wrapper and the Fortran subroutine. Then o9 is available in Octave like any other function:

 octave:1> o9 
 ans = 9

More details

Fortran has different calling conventions from C/C++, and conventions vary from system to system, and perhaps between compilers on the same system. A set of macros hides the details.

F77_XFCN (name, NAME, (args...));

Otherwise, if you are sure it will not generate any signals or run for excessive time, you can use:
F77_FUNC(name,NAME)(args...);

Rather than using "char*" for string argument declarations and one extra "int" declaration at the end for each string length, you need either
F77_CHAR_ARG_DECL
or
F77_CONST_CHAR_ARG_DECL
depending on the const-ness of the argument.

At the end of the argument list, leave the option for a string length declaration if that's what the compiler uses:
F77_CHAR_ARG_LEN_DECL

To call a function with a string argument, you need to use one of the following in place of the string argument:
F77_CHAR_ARG(x) for non-const arg, len = strlen(x)
F77_CONST_CHAR_ARG(x) for const arg, len = strlen(x)
F77_CXX_STRING_ARG(x) for const arg, len = x.lenth()
F77_CHAR_ARG2(x,n) for non-const arg, len = n
F77_CONST_CHAR_ARG2(x,n) for const arg, len = n

Regardless, you need to use the following macro at the end of the argument list for each string you pass. It may or may not be ignored.
F77_CHAR_ARG_LEN(n)

Examples

There are a number of examples in the Octave sources to begin with, such as dozens of the C++ files in liboctave and a couple (balance.cc[1] and qz.cc[2]) in src/DLD-FUNCTIONS. In liboctave/CColVector.cc[3], for example, a call is made to the BLAS subroutine ZGEMV which performs matrix-vector multiplication.

It's possible to get the basic idea from these existing files, but let's look at a simple but complete example.

Say we had a Fortran subroutine TNINE:

           SUBROUTINE TNINE (IOPT, PARMOD, PS, X, Y, Z, BX, BY, BZ)
           INTEGER IOPT
           DOUBLE PRECISION PARMOD(10), PS, X, Y, Z, BX, BY, BZ    
     C     This is just a test subroutine body, to check connexions.
     C     Put the sum of PARMOD in PS, and X, Y, Z into BX, BY, BZ
           INTEGER I
           PS = 0D0
           DO 1 I=1, 10
              PS = PS + PARMOD (I)
      1    CONTINUE
           BX = X
           BY = Y
           BZ = Z
           END

A minimal C++ wrapper would look like:

    #include <octave/oct.h>
    #include "f77-fcn.h"
    extern "C"
     {
       int F77_FUNC (tnine, TNINE) (const int& IOPT, const double* PARMOD,
 				    double& PS,
 				    const double& X, const double& Y,
 				    const double &Z,
 				    double& BX, double& BY, double& BZ );
     }
     DEFUN_DLD (t96, args, ,
 	       "- Loadable Function: [PS, BX, BY, BZ] = t96 (PM, X, Y, Z)\n\
     \n\
     Returns the sum of PM in PS and X, Y, and Z in BX, BY, and BZ.")
     {
       octave_value_list retval;
       const int dummy_integer = 0;
       Matrix pm;
       const double x = args(1).double_value(), y = args(2).double_value(),
       z = args(3).double_value();
       double ps, bx, by, bz;
       pm = args(0).matrix_value ();
       F77_XFCN (tnine, TNINE,
 	        (dummy_integer, pm.fortran_vec(), ps, x, y, z, bx, by, bz) );
       if (f77_exception_encountered)
         {
           error ("unrecoverable error in t96");
           return retval;
         }
       retval(0) = octave_value (ps);
       retval(1) = octave_value (bx);
       retval(2) = octave_value (by);
       retval(3) = octave_value (bz);
       return retval;
     }

Compile this (in the Bourne Again Shell) with

    $ mkoctfile t96.cc tnine.f

and run it in Octave like:

    octave> [p, x, y, z] = t96 (1:10, sqrt (2), pi, e)
    p = 55
    x = 1.4142
    y = 3.1416
    z = 2.7183

The main thing to be added to this approach to extend it to more complicated Fortran subprograms is the way to handle different Fortran data types. For this, go to the OctaveFortranMex page or refer to the examples in the Octave sources cited above.


CategoryCode CategoryExternal