Calling Fortran Routines from C++

Although there are tools that convert Fortran into C code, you may want to call a Fortran routine directly. The next steps demonstrate how Fortran routines can be called from C++.

Calling a Fortran subroutine

The first example shows how a Fortran subroutine is declared and called from a C++ program. The Fortran subroutine is compiled using the GNU compiler with gfortran -c functions.f90 or using the Intel compiler with ifort -c functions.f90.

! functions.f90
subroutine hello()
    implicit none
    !
    print *, "Hello from Fortran!"
end subroutine hello

The main program is written in C++. The interface between Fortran and C++ requires a C wrapper. Note the underscore in the C wrapper and the function call. In contrast to C++, Fortran does not distinguish between capital and lowercase letters. For a matching function declaration, the C wrapper must use lowercase letters and append an underscore to the Fortran function name.

// main.cpp
extern "C" {
    void hello_();
}

int main(int argc, char **argv) {
    hello_();
    return 0;
}

The file main.cpp is compiled with g++ -c main.cpp. The linker is invoked with g++ main.o functions.o -lgfortran. The corresponding commands for the Intel compiler are icpc -c main.cpp and icpc main.o functions.o -lifcore, respectively.

Passing arguments

The second example addresses Fortran's call-by-reference paradigm. The following Fortran function takes a vector and its length as input parameters, adds all elements of a vector and returns the sum.

double precision function sumVector(n, v)
    implicit none
    ! Argument list
    integer, intent(in) :: n
    double precision, intent(in) :: v(n)
    !
    ! Add all vector elements.
    sumVector = sum(v)
end function sumVector

Any argument passed to a Fortran function is passed by reference. This includes all primitive data types, which are typically passed by value in C. Consequently, the C++ driver uses the address operator to pass the integer len. The array is already a pointer and can therefore be passed directly.

#include <iostream>
using namespace std;

extern "C" {
    double sumvector_(const int *n, double *v);
}

int main(int argc, char **argv) {
    int len = 100;
    double *v = new double[len];
    // Initialise.
    for (int i = 0; i < len; i++)
        v[i] = i*1.25;

    double sum = sumvector_(&len, v);
    cout << "sum = " << sum << endl;
    delete[] v;
    return 0;
}

Passing matrices

The third example passes a matrix to the Fortran subroutine, which multiplies each entry of the matrix with a scalar. The C++ driver passes the start address of the two-dimensional array; the Fortran subroutine interprets the contiguous chunk of memory as an n-by-m matrix. Fortran uses column-major storage for higher-dimensional arrays, whereas C++ uses row-major order. The C++ driver initialises the matrix in column-major order (note the order of the loops) in order to have a consistent memory layout.

subroutine scaleMatrix(n, m, A, alpha)
    implicit none
    ! Argument list
    double precision, intent(inout) :: A(n, m)
    integer, intent(in) :: n
    integer, intent(in) :: m
    double precision, intent(in) :: alpha
    !
    ! Scale n-by-m matrix A with scalar alpha
    A =  alpha * A
end subroutine scaleMatrix

    int n = 4, m = 5;
    double A[n][m];
    // Initialise in column major format.
    int cnt = 0;
    for (int j = 0; j < m; j++)
        for (int i = 0; i < n; i++)
            A[i][j] = ++cnt;

    double alpha = 0.25;
    scalematrix_(&n, &m, &A[0][0], &alpha);

Source code

References

Intermixing Fortran and C.

Using C/C++ and Fortran together.